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 {
|
pub fn format(quantity: MetricQuantity) -> String {
|
||||||
let SiUnit(multiplier, unit) = si_unit(quantity);
|
let SiUnit(multiplier, unit) = si_unit(quantity);
|
||||||
let amount = quantity.amount / multiplier;
|
let amount = quantity.amount / multiplier;
|
||||||
|
let amount = format_number(amount);
|
||||||
format!("{amount} {unit}")
|
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)]
|
#[derive(Debug, PartialEq)]
|
||||||
struct SiUnit(f64, &'static str);
|
struct SiUnit(f64, &'static str);
|
||||||
|
|
||||||
|
@ -53,7 +104,7 @@ mod test {
|
||||||
amount: 1.5,
|
amount: 1.5,
|
||||||
unit: Metric::Metre,
|
unit: Metric::Metre,
|
||||||
}));
|
}));
|
||||||
assert_eq!("1000 km", &format(MetricQuantity {
|
assert_eq!("1 000 km", &format(MetricQuantity {
|
||||||
amount: 1_000_000.0,
|
amount: 1_000_000.0,
|
||||||
unit: Metric::Metre,
|
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]
|
#[test]
|
||||||
fn units() {
|
fn units() {
|
||||||
assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity {
|
assert_eq!(SiUnit(0.001, "mm"), si_unit(MetricQuantity {
|
||||||
|
|
Loading…
Reference in New Issue