Format numbers so that they are easier to read
This commit is contained in:
parent
d77a84fef1
commit
f8db69e3b2
|
@ -3,10 +3,61 @@ use crate::units::{Metric, MetricQuantity};
|
|||
pub fn format(quantity: MetricQuantity) -> String {
|
||||
let SiUnit(multiplier, unit) = si_unit(quantity);
|
||||
let amount = quantity.amount / multiplier;
|
||||
|
||||
let amount = format_number(amount);
|
||||
format!("{amount} {unit}")
|
||||
}
|
||||
|
||||
fn format_number(number: f64) -> String {
|
||||
let sign = if number < 0.0 { "-" } else { "" };
|
||||
let number = number.abs();
|
||||
|
||||
// Lower the number of decimal digits as the number grows, to try
|
||||
// to maintain four significant figures
|
||||
let precision = if number < 1.0 {
|
||||
4
|
||||
} else if number < 10.0 {
|
||||
3
|
||||
} else if number < 100.0 {
|
||||
2
|
||||
} else if number < 1000.0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
// Split number into integer and decimal parts for further processing
|
||||
let formatted = format!("{number:.precision$}");
|
||||
let mut formatted = formatted.split('.');
|
||||
let integer = formatted.next().expect("f64 formatted with .precision$ must have a part before '.'");
|
||||
let decimal = if precision > 0 {
|
||||
formatted.next().expect("f64 formatted with .precision$ must have a part after '.' if precision > 0")
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
// Group the integer part into groups of three, e.g. 1000 → 10 000
|
||||
let mut grouped = String::new();
|
||||
let mut group_length = 0;
|
||||
for c in integer.chars().rev() {
|
||||
if group_length == 3 {
|
||||
grouped.push(' ');
|
||||
group_length = 0;
|
||||
}
|
||||
grouped.push(c);
|
||||
group_length += 1;
|
||||
}
|
||||
let grouped: String = grouped.chars().rev().collect();
|
||||
|
||||
// Remove trailing zeroes
|
||||
let decimal = decimal.trim_end_matches('0');
|
||||
|
||||
if decimal.len() == 0 {
|
||||
format!("{sign}{grouped}")
|
||||
} else {
|
||||
format!("{sign}{grouped}.{decimal}")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct SiUnit(f64, &'static str);
|
||||
|
||||
|
@ -53,7 +104,7 @@ mod test {
|
|||
amount: 1.5,
|
||||
unit: Metric::Metre,
|
||||
}));
|
||||
assert_eq!("1000 km", &format(MetricQuantity {
|
||||
assert_eq!("1 000 km", &format(MetricQuantity {
|
||||
amount: 1_000_000.0,
|
||||
unit: Metric::Metre,
|
||||
}));
|
||||
|
@ -68,6 +119,34 @@ mod test {
|
|||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numbers() {
|
||||
assert_eq!(&format_number(0.0001), "0.0001");
|
||||
assert_eq!(&format_number(0.001), "0.001");
|
||||
assert_eq!(&format_number(0.01), "0.01");
|
||||
assert_eq!(&format_number(0.1), "0.1");
|
||||
assert_eq!(&format_number(1.0), "1");
|
||||
assert_eq!(&format_number(10.0), "10");
|
||||
assert_eq!(&format_number(100.0), "100");
|
||||
assert_eq!(&format_number(1_000.0), "1 000");
|
||||
assert_eq!(&format_number(10_000.0), "10 000");
|
||||
assert_eq!(&format_number(100_000.0), "100 000");
|
||||
assert_eq!(&format_number(1_000_000.0), "1 000 000");
|
||||
|
||||
assert_eq!(&format_number(0.00001), "0");
|
||||
assert_eq!(&format_number(1.0001), "1");
|
||||
assert_eq!(&format_number(1.001), "1.001");
|
||||
assert_eq!(&format_number(10.001), "10");
|
||||
assert_eq!(&format_number(10.01), "10.01");
|
||||
assert_eq!(&format_number(100.01), "100");
|
||||
assert_eq!(&format_number(100.1), "100.1");
|
||||
assert_eq!(&format_number(1_000.1), "1 000");
|
||||
|
||||
assert_eq!(&format_number(-1.0), "-1");
|
||||
assert_eq!(&format_number(-100.0), "-100");
|
||||
assert_eq!(&format_number(-1000.0), "-1 000");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn units() {
|
||||
assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity {
|
||||
|
|
Loading…
Reference in New Issue