6 releases (2 stable)
new 1.1.0 | Jan 24, 2025 |
---|---|
1.0.0 | Jan 15, 2025 |
0.3.1 | Jan 7, 2025 |
0.2.0 | Jan 2, 2025 |
0.1.0 | Jan 1, 2025 |
#312 in Parser implementations
710 downloads per month
Used in qcp
64KB
1K
SLoC
Numeric conversions for engineering notation and RKM code.
Overview
In engineering applications it is common to express quantities relative to the next-lower power of 1000, described by an SI (metric) prefix.
This is normally done by writing the SI multiplier after the quantity. In the "RKM code" variant, the SI multiplier replaces the decimal point.
For example:
Number | Engineering | RKM |
---|---|---|
42 | 42 | 42 |
999 | 999 | 999 |
1000 | 1k | 1k |
1500 | 1.5k | 1k5 |
42900 | 42.9k | 42k9 |
2340000 | 2.34M | 2M34 |
And so on going up the SI prefixes, including the new ones R (1027) and Q (1030) which were added in 2022.
This crate exists to support convenient conversion of numbers to/from engineering and RKM notation. The intended use case is for parsing user-entered configuration.
Detail
This crate is centred around the EngineeringQuantity<T>
type. This type supports comparisons via PartialEq
, Eq
, PartialOrd
and Ord
, though if you want to perform actual maths you are probably better off converting to int or Ratio
.
Storage
- The generic parameter
T
specifies the storage type to use for the significand. This can be any primitive integer except fori8
oru8
, which are too small to be useful.- For example,
EngineeringQuantity<u64>
.
- For example,
- The exponent is always stored as an
i8
. This can range from -10 (q) to +10 (Q); going beyond that will likely causeOverflow
orUnderflow
errors.
Conversions
You can convert an EngineeringQuantity
to:
- integer types, truncating any fraction:
- directly
into
typeT
, or a larger integer type (one which implementsFrom<T>
); - any integer type using the
num_traits::ToPrimitive
trait (to_i32()
and friends, which apply an overflow check);
- directly
- String, optionally via the
DisplayAdapter
type to control the formatting; - another
EngineeringQuantity
(convert
if the destination storage type is larger;try_convert
if it is smaller); f32
andf64
(with an over/underflow check);num_rational::Ratio
(with an over/underflow check);- its component parts, as a tuple
(<T>, i8)
(seeto_raw
).
You can create an EngineeringQuantity
from:
- type
T
, or a smaller integer type (one which implementsInto<T>
); - String or
&str
, which autodetects both standard and RKM code variants; num_rational::Ratio
, which requires the denominator be a power of 1000;- its component parts
(<T>, i8)
(seefrom_raw
), which will overflow if the converted number cannot fit intoT
.
Supported integer types may be converted directly to string via the EngineeringRepr
convenience trait.
Or, if you prefer, here are the type relations in diagram form:
┌────────────────────┐
│ integers │
└────────────────────┘
▲ I
╵ I [impl]
╵ I
▼ ▼
┌───────────────────────────────────────────┐ ┌─────────────────────┐
│ EngineeringQuantity<T> │ │ EngineeringRepr │
│ │ │ (convenience trait) │
└───────────────────────────────────────────┘ └─────────────────────┘
▲ ▲ ╵ ▲ │ │
╵ ╵ ╵ ╵ │ │ to_eng()
╵ ╵ ╵ ╵ │ │ to_rkm()
▼ ▼ ▼ ╵ ▼ ▼
┌─────────────┐ ┌────────────────┐ ┌───────┐ ╵ ┌───────────────────┐
│ "raw" tuple │ │ num_rational:: │ │ f32 │ ╵ │ DisplayAdapter<T> │
│ (T, i8) │ │ Ratio<T> │ │ f64 │ ╵ │ │
└─────────────┘ └────────────────┘ └───────┘ ╵ └───────────────────┘
╵ │
╵ │
▼ ▼
┌───────────────────┐
│ String │
└───────────────────┘
Serialization
The serde
feature flag adds support for EngineeringQuantity
:
- Serialization as a String (only);
- Deserialization from a String or an integer
Deserialization is subject to range checks and will fail if, for example, the number does not fit into the underlying storage type.
If you need more control than this, you may wish to specify a custom serializer / deserializer.
Examples
String to number
use engineering_repr::EngineeringQuantity as EQ;
use std::str::FromStr as _;
use num_rational::Ratio;
// Standard notation
let eq = EQ::<i64>::from_str("1.5k").unwrap();
assert_eq!(i64::try_from(eq).unwrap(), 1500);
// RKM style notation
let eq2 = EQ::<i64>::from_str("1k5").unwrap();
assert_eq!(eq, eq2);
// Conversion to the nearest integer
let eq3 = EQ::<i32>::from_str("3m").unwrap();
assert_eq!(i32::try_from(eq3).unwrap(), 0);
// Convert to Ratio
let r : Ratio<i32> = eq3.try_into().unwrap();
assert_eq!(r, Ratio::new(3, 1000)); // => 3 / 1000
// Convert to float
let f : f64 = eq3.try_into().unwrap();
assert_eq!(f, 0.003); // caution, not all float conversions will work out exactly
Number to string
use engineering_repr::EngineeringQuantity as EQ;
// default precision (3 places, "sloppy" omitting trailing zeroes)
let ee1 = EQ::<i32>::from(1200);
assert_eq!(ee1.to_string(), "1.2k");
// strict precision
assert_eq!(ee1.with_strict_precision(3).to_string(), "1.20k");
// explicit precision
let ee2 = EQ::<i32>::from(1234567);
assert_eq!(ee2.with_precision(2).to_string(), "1.2M");
// RKM style
assert_eq!(ee2.rkm_with_precision(2).to_string(), "1M2");
// Zero precision means "automatic, lossless"
assert_eq!(ee2.with_precision(0).to_string(), "1.234567M");
assert_eq!(ee2.rkm_with_precision(0).to_string(), "1M234567");
Integer directly to string via convenience trait
use engineering_repr::EngineeringRepr as _;
assert_eq!("123.45k", 123456.to_eng(5));
assert_eq!("123.456k", 123456.to_eng(0)); // automatic precision
assert_eq!("123k4", 123456.to_rkm(4));
Serialization
#[cfg(feature="serde")] // This functionality requires the `serde` feature flag
{
use engineering_repr::EngineeringQuantity as EQ;
let eq1 = EQ::<i32>::from(1200);
// Serialization to string
assert_eq!(serde_json::to_string(&eq1).unwrap(), "\"1.2k\"");
// Deserialization from string
assert_eq!(serde_json::from_str::<EQ<i32>>("\"1.2k\"").unwrap(), eq1);
// Deserialization from integer
assert_eq!(serde_json::from_str::<EQ<i32>>("1200").unwrap(), eq1);
}
Limitations
- Multipliers which are not a power of 1000 (da, h, d, c) are not supported.
Alternatives
- human-repr is great for converting numbers to human-friendly representations.
- humanize-rs is great for converting some human-friendly representations to numbers, though engineering-repr offers more flexibility.
Dependencies
~0.7–1.3MB
~30K SLoC