Skip to content

Commit 5814bf2

Browse files
committed
fix: F56 Display trait new algorithm to display 12 sig figs
1 parent e020dd3 commit 5814bf2

File tree

4 files changed

+62
-14
lines changed

4 files changed

+62
-14
lines changed

builtins/src/types/numbers.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,52 @@ mod tests {
9696
)
9797
}
9898

99+
#[test]
100+
fn test_f56_formatting() {
101+
// F56 should have a maximum of 12 decimal digits displayed
102+
// And the 12th digit should be rounded using the 13th digit
103+
// If we input an f64 with the 13th digit of 5
104+
// The nearest F56 representation might have the 13th digit become 4 or 6
105+
// So be wary that rounding may not occur as expected
106+
// 3.999_999_999_995 rounds down instead of rounding up to 4
107+
let map_from_value_to_expected_string = [
108+
// (3.999_999_999_995, "4"), // bad rounding
109+
(399.999_999_999_58527_f64, "400"),
110+
(399.999_999_999_585_f64, "400"),
111+
(399.999_999_999_58_f64, "400"),
112+
(399.999_999_999_6_f64, "400"),
113+
(399.999_999_999_f64, "399.999999999"),
114+
(
115+
0.000_000_012_312_312_412_412_312_3_f64,
116+
"0.0000000123123124124",
117+
),
118+
(0.000_000_012_312_312_452_57_f64, "0.0000000123123124526"),
119+
(399_999_999.999_58_f64, "400000000"),
120+
(
121+
399_999_999_999_600_000_000_000_000_000_000_0_f64,
122+
"4000000000000000000000000000000000",
123+
),
124+
(
125+
399_999_999_999_600_000_000_000_000_000_000_0_f64,
126+
"4000000000000000000000000000000000",
127+
),
128+
// special values
129+
(f64::NAN, "NaN"),
130+
(f64::INFINITY, "inf"),
131+
(f64::NEG_INFINITY, "-inf"),
132+
(0.0, "0"),
133+
(-0.0, "-0"),
134+
(f64::MIN_POSITIVE, "0"),
135+
(F56::MIN_POSITIVE.into(), "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002983336292479999"),
136+
];
137+
for (f, expected) in map_from_value_to_expected_string.iter() {
138+
// println!("{:?}", f);
139+
let f56 = F56::from(*f);
140+
let formatted = format!("{:}", f56);
141+
assert_eq!(formatted, *expected);
142+
}
143+
}
144+
99145
#[test]
100146
fn test_i32_conversions_rust_to_value() {
101147
let mut vm = new_slosh_vm();

slosh/benches/all.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ fn run_float_script(n: usize, m: f32, expected: f64) {
9292
let last = run_reader(&mut reader);
9393
match last {
9494
Ok(Value::Float(f)) => {
95-
assert_eq!(f64::from(f), expected);
95+
assert_eq!(f.to_string(), expected.to_string());
96+
// assert_eq!(f64::from(f), expected);
9697
}
9798
_ => {
9899
panic!("Not a float");

slosh/benches/bench.slosh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
(def pu (eval-pol 100 0.5))
66
(prn "(eval-pol 100 0.5) =>\n" pu)
7-
(prn "passed: " (equal? pu 399.99999999958527))
7+
(prn "passed: " (equal? pu 400))
88

99
(def pu (eval-pol 1000 0.05))
1010
(prn "(eval-pol 1000 0.05) =>\n" pu)
11-
(prn "passed: " (equal? pu 2105.2631578924484))
11+
(prn "passed: " (equal? pu 2105.26315789))
1212

1313
(def pu (eval-pol 10000 0.2))
1414
(prn "(eval-pol 10000 0.2) =>\n" pu)
15-
(prn "passed: " (equal? pu 24999.999999998603))
15+
(prn "passed: " (equal? pu 2500))
1616

vm/src/value.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,18 @@ impl Display for F56 {
152152
// F56 only has 12, meaning that .0023 appears as .0022999999999999687
153153
// if F56 knew to only print 12 digits then it would be fine,
154154
// but when going through f64, it thinks it has 15 perfect digits.
155+
let as_f64 = f64::from(*self);
155156

156-
// We can set the precision to 12 but that increases numbers like 1.0 to 1.0000000000000
157-
// So let's do that, and remove the trailing zeros and decimal points added on
158-
let first_pass = format!("{:.*}", F56::DIGITS, f64::from(*self));
159-
// remove trailing zeros and trailing decimal point
160-
let second_pass = if first_pass.contains('.') {
161-
first_pass.trim_end_matches('0').trim_end_matches('.')
162-
} else {
163-
&first_pass
164-
};
165-
write!(f, "{}", second_pass)
157+
// Handle special cases by printing the f64 value
158+
if as_f64.is_nan() || as_f64.is_infinite() || as_f64 == 0.0 {
159+
return write!(f, "{}", as_f64);
160+
}
161+
162+
// round to a max of F56::DIGITS sig figs
163+
let exponent_value = as_f64.log10().floor() as i32; // the number after 'e' in scientific notation
164+
let scale = 10f64.powi(exponent_value - F56::DIGITS as i32 + 1);
165+
let rounded = (as_f64 / scale).round() * scale;
166+
write!(f, "{}", rounded)
166167
}
167168
}
168169
impl From<f64> for F56 {

0 commit comments

Comments
 (0)