Skip to content

Commit 3d34e17

Browse files
committed
core: use banker's rounding for the exact mode in flt2dec.
For the shortest mode the IEEE 754 decoder already provides an exact rounding range accounting for banker's rounding, but it was not the case for the exact mode. This commit alters the exact mode algorithm for Dragon so that any number ending at `...x5000...` with even `x` and infinite zeroes will round to `...x` instead of `...(x+1)` as it was. Grisu is not affected by this change because this halfway case always results in the failure for Grisu.
1 parent a641b05 commit 3d34e17

File tree

2 files changed

+23
-6
lines changed

2 files changed

+23
-6
lines changed

src/libcore/num/flt2dec/strategy/dragon.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,11 @@ pub fn format_exact(d: &Decoded, buf: &mut [u8], limit: i16) -> (/*#digits*/ usi
307307
}
308308

309309
// rounding up if we stop in the middle of digits
310-
if mant >= *scale.mul_small(5) {
310+
// if the following digits are exactly 5000..., check the prior digit and try to
311+
// round to even (i.e. avoid rounding up when the prior digit is even).
312+
let order = mant.cmp(scale.mul_small(5));
313+
if order == Ordering::Greater || (order == Ordering::Equal &&
314+
(len == 0 || buf[len-1] & 1 == 1)) {
311315
// if rounding up changes the length, the exponent should also change.
312316
// but we've been requested a fixed number of digits, so do not alter the buffer...
313317
if let Some(c) = round_up(buf, len) {

src/libcoretest/num/flt2dec/mod.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,17 @@ fn check_exact<F, T>(mut f: F, v: T, vstr: &str, expected: &[u8], expectedk: i16
105105
bytes::copy_memory(&expected[..i], &mut expected_);
106106
let mut expectedk_ = expectedk;
107107
if expected[i] >= b'5' {
108-
// if this returns true, expected_[..i] is all `9`s and being rounded up.
109-
// we should always return `100..00` (`i` digits) instead, since that's
110-
// what we can came up with `i` digits anyway. `round_up` assumes that
111-
// the adjustment to the length is done by caller, which we simply ignore.
112-
if let Some(_) = round_up(&mut expected_, i) { expectedk_ += 1; }
108+
// check if this is a rounding-to-even case.
109+
// we avoid rounding ...x5000... (with infinite zeroes) to ...(x+1) when x is even.
110+
if !(i+1 < expected.len() && expected[i-1] & 1 == 0 &&
111+
expected[i] == b'5' &&
112+
expected[i+1] == b' ') {
113+
// if this returns true, expected_[..i] is all `9`s and being rounded up.
114+
// we should always return `100..00` (`i` digits) instead, since that's
115+
// what we can came up with `i` digits anyway. `round_up` assumes that
116+
// the adjustment to the length is done by caller, which we simply ignore.
117+
if let Some(_) = round_up(&mut expected_, i) { expectedk_ += 1; }
118+
}
113119
}
114120

115121
try_exact!(f(&decoded) => &mut buf, &expected_[..i], expectedk_;
@@ -243,6 +249,7 @@ pub fn f32_exact_sanity_test<F>(mut f: F)
243249
let minf32 = f32::ldexp(1.0, -149);
244250

245251
check_exact!(f(0.1f32) => b"100000001490116119384765625 ", 0);
252+
check_exact!(f(0.5f32) => b"5 ", 0);
246253
check_exact!(f(1.0f32/3.0) => b"3333333432674407958984375 ", 0);
247254
check_exact!(f(3.141592f32) => b"31415920257568359375 ", 1);
248255
check_exact!(f(3.141592e17f32) => b"314159196796878848 ", 18);
@@ -348,6 +355,7 @@ pub fn f64_exact_sanity_test<F>(mut f: F)
348355

349356
check_exact!(f(0.1f64) => b"1000000000000000055511151231257827021181", 0);
350357
check_exact!(f(0.45f64) => b"4500000000000000111022302462515654042363", 0);
358+
check_exact!(f(0.5f64) => b"5 ", 0);
351359
check_exact!(f(0.95f64) => b"9499999999999999555910790149937383830547", 0);
352360
check_exact!(f(100.0f64) => b"1 ", 3);
353361
check_exact!(f(999.5f64) => b"9995000000000000000000000000000000000000", 3);
@@ -1032,6 +1040,11 @@ pub fn to_exact_fixed_str_test<F>(mut f_: F)
10321040
assert_eq!(to_string(f, 999.5, Minus, 3, false), "999.500");
10331041
assert_eq!(to_string(f, 999.5, Minus, 30, false), "999.500000000000000000000000000000");
10341042

1043+
assert_eq!(to_string(f, 0.5, Minus, 0, false), "1");
1044+
assert_eq!(to_string(f, 0.5, Minus, 1, false), "0.5");
1045+
assert_eq!(to_string(f, 0.5, Minus, 2, false), "0.50");
1046+
assert_eq!(to_string(f, 0.5, Minus, 3, false), "0.500");
1047+
10351048
assert_eq!(to_string(f, 0.95, Minus, 0, false), "1");
10361049
assert_eq!(to_string(f, 0.95, Minus, 1, false), "0.9"); // because it really is less than 0.95
10371050
assert_eq!(to_string(f, 0.95, Minus, 2, false), "0.95");

0 commit comments

Comments
 (0)