Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_units/tests/humanise.rs

5.7 KiB, 1 run

created by r1870400018:17588, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! External-oracle tests for engineering notation.
2//!
3//! The expected prefixes and values come from the SI brochure's prefix table
4//! and from the IEC binary prefixes, not from the implementation: milli is
5//! 10^-3, pico is 10^-12, kilo is 10^3, giga is 10^9, and a kibi is 1024. So
6//! 0.005 is 5 milli, 2.8e-10 is 280 pico, and 1024 is 1 kibi. The table stops
7//! at atto below and exa above, and a magnitude outside that span has no
8//! prefix at all.
9
10use oxedyne_fe2o3_units::{
11 scale::{
12 Mag,
13 Scale,
14 ScaleBasis,
15 },
16 si::SI,
17 system::Units,
18};
19
20use oxedyne_fe2o3_core::prelude::*;
21
22/// Asserts two floats agree to a relative tolerance.
23fn close(a: f64, b: f64) -> bool {
24 if b == 0.0 {
25 a.abs() < 1.0e-12
26 } else {
27 ((a - b) / b).abs() < 1.0e-9
28 }
29}
30
31#[test]
32fn test_humanise_decimal_prefixes_01() -> Outcome<()> {
33 // 0.005 is 5 x 10^-3, and 10^-3 is milli.
34 let h = res!(Mag::one_decimal(0.005, 2)).humanise();
35 assert!(close(h.val, 5.0), "got {}", h.val);
36 assert_eq!(h.prefix(), "m");
37 // 2.8e-10 is 280 x 10^-12, and 10^-12 is pico.
38 let h = res!(Mag::one_decimal(2.8e-10, 2)).humanise();
39 assert!(close(h.val, 280.0), "got {}", h.val);
40 assert_eq!(h.prefix(), "p");
41 // 123456 is 123.456 x 10^3, and 10^3 is kilo; three figures keep 123.
42 let h = res!(Mag::one_decimal(123456.0, 3)).humanise();
43 assert!(close(h.val, 123.0), "got {}", h.val);
44 assert_eq!(h.prefix(), "k");
45 // 1234 mega is 1.234 giga.
46 let h = res!(Mag::mega(1234.0, 4)).humanise();
47 assert!(close(h.val, 1.234), "got {}", h.val);
48 assert_eq!(h.prefix(), "G");
49 Ok(())
50}
51
52/// A prefix names a magnitude, so a negative value takes the same one its
53/// positive counterpart does and keeps its sign in the value.
54#[test]
55fn test_humanise_negative_01() -> Outcome<()> {
56 let h = res!(Mag::one_decimal(-0.005, 2)).humanise();
57 assert!(close(h.val, -5.0), "got {}", h.val);
58 assert_eq!(h.prefix(), "m");
59 let h = res!(Mag::one_decimal(-123456.0, 3)).humanise();
60 assert!(close(h.val, -123.0), "got {}", h.val);
61 assert_eq!(h.prefix(), "k");
62 let h = res!(Mag::mega(-1234.0, 4)).humanise();
63 assert!(close(h.val, -1.234), "got {}", h.val);
64 assert_eq!(h.prefix(), "G");
65 Ok(())
66}
67
68/// A zero has no scale to find, and must come back as a zero with no prefix
69/// rather than as a not-a-number.
70#[test]
71fn test_humanise_zero_01() -> Outcome<()> {
72 let h = res!(Mag::one_decimal(0.0, 3)).humanise();
73 assert_eq!(h.val, 0.0);
74 assert_eq!(h.prefix(), "");
75 let h = res!(Mag::milli(0.0, 3)).humanise();
76 assert_eq!(h.val, 0.0);
77 assert_eq!(h.prefix(), "");
78 Ok(())
79}
80
81/// The prefix table runs from atto to exa. A magnitude beyond either end is
82/// returned unscaled and unprefixed, and above all does not bring the process
83/// down.
84#[test]
85fn test_humanise_beyond_the_prefix_table_01() -> Outcome<()> {
86 for v in [1.0e21f64, -1.0e21, 1.0e-21, -1.0e-21, 1.0e30, 1.0e-30] {
87 let h = res!(Mag::one_decimal(v, 3)).humanise();
88 assert_eq!(h.prefix(), "", "{:e} should have no prefix", v);
89 assert!(close(h.val, v), "{:e} came back as {:e}", v, h.val);
90 }
91 Ok(())
92}
93
94/// Every prefix in the decimal table is reachable, and each is a thousandfold
95/// step from the last.
96#[test]
97fn test_humanise_reaches_every_prefix_01() -> Outcome<()> {
98 let want = [
99 (-18i32, "a"), (-15, "f"), (-12, "p"), (-9, "n"), (-6, "\u{00b5}"),
100 (-3, "m"), (0, ""), (3, "k"), (6, "M"), (9, "G"), (12, "T"),
101 (15, "P"), (18, "E"),
102 ];
103 for (exp, prefix) in want {
104 let v = match format!("2.5e{}", exp).parse::<f64>() {
105 Ok(v) => v,
106 Err(_) => continue,
107 };
108 let h = res!(Mag::one_decimal(v, 2)).humanise();
109 assert_eq!(h.prefix(), prefix, "2.5e{} should read in {}", exp, prefix);
110 assert!(close(h.val, 2.5), "2.5e{} came back as {}", exp, h.val);
111 }
112 Ok(())
113}
114
115/// A kibi is 1024, so 1024 bytes read in binary is 1 KiB.
116#[test]
117fn test_humanise_binary_prefixes_01() -> Outcome<()> {
118 let h = res!(Units::<SI>::bytes(1024.0, 4)).humanise();
119 assert!(close(h.val(), 1.0), "got {}", h.val());
120 assert_eq!(h.prefix(), "Ki");
121 assert_eq!(h.symbol(), "B");
122 // A mebi is 1024^2.
123 let h = res!(Units::<SI>::bytes(1024.0 * 1024.0, 4)).humanise();
124 assert!(close(h.val(), 1.0), "got {}", h.val());
125 assert_eq!(h.prefix(), "Mi");
126 Ok(())
127}
128
129/// A prefix lookup that has no entry reports it, rather than bringing the
130/// process down.
131#[test]
132fn test_dec_exp_lookup_reports_a_miss_01() -> Outcome<()> {
133 let dec = Scale::One(ScaleBasis::Decimal);
134 assert!(dec.dec_exp_lookup(3).is_ok());
135 assert!(dec.dec_exp_lookup(21).is_err());
136 assert!(dec.dec_exp_lookup(-21).is_err());
137 assert!(dec.dec_exp_lookup(4).is_err());
138 let bin = Scale::One(ScaleBasis::Binary);
139 assert!(bin.dec_exp_lookup(3).is_ok());
140 assert!(bin.dec_exp_lookup(-3).is_err());
141 Ok(())
142}
143
144/// Normalising puts the leading digit in the units place and reports the
145/// decade it came from, for either sign.
146#[test]
147fn test_normalise_01() -> Outcome<()> {
148 let (sig, exp) = res!(Mag::one_decimal(1234.0, 4)).normalise();
149 assert!(close(sig, 1.234), "got {}", sig);
150 assert_eq!(exp, 3);
151 let (sig, exp) = res!(Mag::one_decimal(-1234.0, 4)).normalise();
152 assert!(close(sig, -1.234), "got {}", sig);
153 assert_eq!(exp, 3);
154 // 0.0475 is 4.75 x 10^-2, and two figures round the tie away from zero.
155 let (sig, exp) = res!(Mag::one_decimal(-0.0475, 2)).normalise();
156 assert!(close(sig, -4.8), "got {}", sig);
157 assert_eq!(exp, -2);
158 // A zero has no decade.
159 let (sig, exp) = res!(Mag::one_decimal(0.0, 3)).normalise();
160 assert_eq!(sig, 0.0);
161 assert_eq!(exp, 0);
162 Ok(())
163}