Skip to content

Commit e702534

Browse files
committed
Auto merge of #102935 - ajtribick:display-float-0.5-fixed-0, r=scottmcm
Fix inconsistent rounding of 0.5 when formatted to 0 decimal places As described in #70336, when displaying values to zero decimal places the value of 0.5 is rounded to 1, which is inconsistent with the display of other half-integer values which round to even. From testing the flt2dec implementation, it looks like this comes down to the condition in the fixed-width Dragon implementation where an empty buffer is treated as a case to apply rounding up. I believe the change below fixes it and updates only the relevant tests. Nevertheless I am aware this is very much a core piece of functionality, so please take a very careful look to make sure I haven't missed anything. I hope this change does not break anything in the wider ecosystem as having a consistent rounding behaviour in floating point formatting is in my opinion a useful feature to have. Resolves #70336
2 parents 3b91b1a + aa9837b commit e702534

File tree

3 files changed

+125
-5
lines changed

3 files changed

+125
-5
lines changed

Diff for: library/core/src/num/flt2dec/strategy/dragon.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ pub fn format_exact<'a>(
366366
if order == Ordering::Greater
367367
|| (order == Ordering::Equal
368368
// SAFETY: `buf[len-1]` is initialized.
369-
&& (len == 0 || unsafe { buf[len - 1].assume_init() } & 1 == 1))
369+
&& len > 0 && unsafe { buf[len - 1].assume_init() } & 1 == 1)
370370
{
371371
// if rounding up changes the length, the exponent should also change.
372372
// but we've been requested a fixed number of digits, so do not alter the buffer...

Diff for: library/core/tests/fmt/float.rs

+122-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ fn test_format_f64() {
55
assert_eq!("10", format!("{:.0}", 9.9f64));
66
assert_eq!("9.8", format!("{:.1}", 9.849f64));
77
assert_eq!("9.9", format!("{:.1}", 9.851f64));
8-
assert_eq!("1", format!("{:.0}", 0.5f64));
8+
assert_eq!("0", format!("{:.0}", 0.5f64));
99
assert_eq!("1.23456789e6", format!("{:e}", 1234567.89f64));
1010
assert_eq!("1.23456789e3", format!("{:e}", 1234.56789f64));
1111
assert_eq!("1.23456789E6", format!("{:E}", 1234567.89f64));
@@ -24,14 +24,74 @@ fn test_format_f64() {
2424
assert_eq!("1234.6", format!("{:.1?}", 1234.56789f64));
2525
}
2626

27+
#[test]
28+
fn test_format_f64_rounds_ties_to_even() {
29+
assert_eq!("0", format!("{:.0}", 0.5f64));
30+
assert_eq!("2", format!("{:.0}", 1.5f64));
31+
assert_eq!("2", format!("{:.0}", 2.5f64));
32+
assert_eq!("4", format!("{:.0}", 3.5f64));
33+
assert_eq!("4", format!("{:.0}", 4.5f64));
34+
assert_eq!("6", format!("{:.0}", 5.5f64));
35+
assert_eq!("128", format!("{:.0}", 127.5f64));
36+
assert_eq!("128", format!("{:.0}", 128.5f64));
37+
assert_eq!("0.2", format!("{:.1}", 0.25f64));
38+
assert_eq!("0.8", format!("{:.1}", 0.75f64));
39+
assert_eq!("0.12", format!("{:.2}", 0.125f64));
40+
assert_eq!("0.88", format!("{:.2}", 0.875f64));
41+
assert_eq!("0.062", format!("{:.3}", 0.062f64));
42+
assert_eq!("-0", format!("{:.0}", -0.5f64));
43+
assert_eq!("-2", format!("{:.0}", -1.5f64));
44+
assert_eq!("-2", format!("{:.0}", -2.5f64));
45+
assert_eq!("-4", format!("{:.0}", -3.5f64));
46+
assert_eq!("-4", format!("{:.0}", -4.5f64));
47+
assert_eq!("-6", format!("{:.0}", -5.5f64));
48+
assert_eq!("-128", format!("{:.0}", -127.5f64));
49+
assert_eq!("-128", format!("{:.0}", -128.5f64));
50+
assert_eq!("-0.2", format!("{:.1}", -0.25f64));
51+
assert_eq!("-0.8", format!("{:.1}", -0.75f64));
52+
assert_eq!("-0.12", format!("{:.2}", -0.125f64));
53+
assert_eq!("-0.88", format!("{:.2}", -0.875f64));
54+
assert_eq!("-0.062", format!("{:.3}", -0.062f64));
55+
56+
assert_eq!("2e0", format!("{:.0e}", 1.5f64));
57+
assert_eq!("2e0", format!("{:.0e}", 2.5f64));
58+
assert_eq!("4e0", format!("{:.0e}", 3.5f64));
59+
assert_eq!("4e0", format!("{:.0e}", 4.5f64));
60+
assert_eq!("6e0", format!("{:.0e}", 5.5f64));
61+
assert_eq!("1.28e2", format!("{:.2e}", 127.5f64));
62+
assert_eq!("1.28e2", format!("{:.2e}", 128.5f64));
63+
assert_eq!("-2e0", format!("{:.0e}", -1.5f64));
64+
assert_eq!("-2e0", format!("{:.0e}", -2.5f64));
65+
assert_eq!("-4e0", format!("{:.0e}", -3.5f64));
66+
assert_eq!("-4e0", format!("{:.0e}", -4.5f64));
67+
assert_eq!("-6e0", format!("{:.0e}", -5.5f64));
68+
assert_eq!("-1.28e2", format!("{:.2e}", -127.5f64));
69+
assert_eq!("-1.28e2", format!("{:.2e}", -128.5f64));
70+
71+
assert_eq!("2E0", format!("{:.0E}", 1.5f64));
72+
assert_eq!("2E0", format!("{:.0E}", 2.5f64));
73+
assert_eq!("4E0", format!("{:.0E}", 3.5f64));
74+
assert_eq!("4E0", format!("{:.0E}", 4.5f64));
75+
assert_eq!("6E0", format!("{:.0E}", 5.5f64));
76+
assert_eq!("1.28E2", format!("{:.2E}", 127.5f64));
77+
assert_eq!("1.28E2", format!("{:.2E}", 128.5f64));
78+
assert_eq!("-2E0", format!("{:.0E}", -1.5f64));
79+
assert_eq!("-2E0", format!("{:.0E}", -2.5f64));
80+
assert_eq!("-4E0", format!("{:.0E}", -3.5f64));
81+
assert_eq!("-4E0", format!("{:.0E}", -4.5f64));
82+
assert_eq!("-6E0", format!("{:.0E}", -5.5f64));
83+
assert_eq!("-1.28E2", format!("{:.2E}", -127.5f64));
84+
assert_eq!("-1.28E2", format!("{:.2E}", -128.5f64));
85+
}
86+
2787
#[test]
2888
fn test_format_f32() {
2989
assert_eq!("1", format!("{:.0}", 1.0f32));
3090
assert_eq!("9", format!("{:.0}", 9.4f32));
3191
assert_eq!("10", format!("{:.0}", 9.9f32));
3292
assert_eq!("9.8", format!("{:.1}", 9.849f32));
3393
assert_eq!("9.9", format!("{:.1}", 9.851f32));
34-
assert_eq!("1", format!("{:.0}", 0.5f32));
94+
assert_eq!("0", format!("{:.0}", 0.5f32));
3595
assert_eq!("1.2345679e6", format!("{:e}", 1234567.89f32));
3696
assert_eq!("1.2345679e3", format!("{:e}", 1234.56789f32));
3797
assert_eq!("1.2345679E6", format!("{:E}", 1234567.89f32));
@@ -50,6 +110,66 @@ fn test_format_f32() {
50110
assert_eq!("1234.6", format!("{:.1?}", 1234.56789f32));
51111
}
52112

113+
#[test]
114+
fn test_format_f32_rounds_ties_to_even() {
115+
assert_eq!("0", format!("{:.0}", 0.5f32));
116+
assert_eq!("2", format!("{:.0}", 1.5f32));
117+
assert_eq!("2", format!("{:.0}", 2.5f32));
118+
assert_eq!("4", format!("{:.0}", 3.5f32));
119+
assert_eq!("4", format!("{:.0}", 4.5f32));
120+
assert_eq!("6", format!("{:.0}", 5.5f32));
121+
assert_eq!("128", format!("{:.0}", 127.5f32));
122+
assert_eq!("128", format!("{:.0}", 128.5f32));
123+
assert_eq!("0.2", format!("{:.1}", 0.25f32));
124+
assert_eq!("0.8", format!("{:.1}", 0.75f32));
125+
assert_eq!("0.12", format!("{:.2}", 0.125f32));
126+
assert_eq!("0.88", format!("{:.2}", 0.875f32));
127+
assert_eq!("0.062", format!("{:.3}", 0.062f32));
128+
assert_eq!("-0", format!("{:.0}", -0.5f32));
129+
assert_eq!("-2", format!("{:.0}", -1.5f32));
130+
assert_eq!("-2", format!("{:.0}", -2.5f32));
131+
assert_eq!("-4", format!("{:.0}", -3.5f32));
132+
assert_eq!("-4", format!("{:.0}", -4.5f32));
133+
assert_eq!("-6", format!("{:.0}", -5.5f32));
134+
assert_eq!("-128", format!("{:.0}", -127.5f32));
135+
assert_eq!("-128", format!("{:.0}", -128.5f32));
136+
assert_eq!("-0.2", format!("{:.1}", -0.25f32));
137+
assert_eq!("-0.8", format!("{:.1}", -0.75f32));
138+
assert_eq!("-0.12", format!("{:.2}", -0.125f32));
139+
assert_eq!("-0.88", format!("{:.2}", -0.875f32));
140+
assert_eq!("-0.062", format!("{:.3}", -0.062f32));
141+
142+
assert_eq!("2e0", format!("{:.0e}", 1.5f32));
143+
assert_eq!("2e0", format!("{:.0e}", 2.5f32));
144+
assert_eq!("4e0", format!("{:.0e}", 3.5f32));
145+
assert_eq!("4e0", format!("{:.0e}", 4.5f32));
146+
assert_eq!("6e0", format!("{:.0e}", 5.5f32));
147+
assert_eq!("1.28e2", format!("{:.2e}", 127.5f32));
148+
assert_eq!("1.28e2", format!("{:.2e}", 128.5f32));
149+
assert_eq!("-2e0", format!("{:.0e}", -1.5f32));
150+
assert_eq!("-2e0", format!("{:.0e}", -2.5f32));
151+
assert_eq!("-4e0", format!("{:.0e}", -3.5f32));
152+
assert_eq!("-4e0", format!("{:.0e}", -4.5f32));
153+
assert_eq!("-6e0", format!("{:.0e}", -5.5f32));
154+
assert_eq!("-1.28e2", format!("{:.2e}", -127.5f32));
155+
assert_eq!("-1.28e2", format!("{:.2e}", -128.5f32));
156+
157+
assert_eq!("2E0", format!("{:.0E}", 1.5f32));
158+
assert_eq!("2E0", format!("{:.0E}", 2.5f32));
159+
assert_eq!("4E0", format!("{:.0E}", 3.5f32));
160+
assert_eq!("4E0", format!("{:.0E}", 4.5f32));
161+
assert_eq!("6E0", format!("{:.0E}", 5.5f32));
162+
assert_eq!("1.28E2", format!("{:.2E}", 127.5f32));
163+
assert_eq!("1.28E2", format!("{:.2E}", 128.5f32));
164+
assert_eq!("-2E0", format!("{:.0E}", -1.5f32));
165+
assert_eq!("-2E0", format!("{:.0E}", -2.5f32));
166+
assert_eq!("-4E0", format!("{:.0E}", -3.5f32));
167+
assert_eq!("-4E0", format!("{:.0E}", -4.5f32));
168+
assert_eq!("-6E0", format!("{:.0E}", -5.5f32));
169+
assert_eq!("-1.28E2", format!("{:.2E}", -127.5f32));
170+
assert_eq!("-1.28E2", format!("{:.2E}", -128.5f32));
171+
}
172+
53173
fn is_exponential(s: &str) -> bool {
54174
s.contains("e") || s.contains("E")
55175
}

Diff for: library/core/tests/num/flt2dec/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ where
138138

139139
// check exact rounding for zero- and negative-width cases
140140
let start;
141-
if expected[0] >= b'5' {
141+
if expected[0] > b'5' {
142142
try_fixed!(f(&decoded) => &mut buf, expectedk, b"1", expectedk + 1;
143143
"zero-width rounding-up mismatch for v={v}: \
144144
actual {actual:?}, expected {expected:?}",
@@ -1007,7 +1007,7 @@ where
10071007
assert_eq!(to_string(f, 999.5, Minus, 3), "999.500");
10081008
assert_eq!(to_string(f, 999.5, Minus, 30), "999.500000000000000000000000000000");
10091009

1010-
assert_eq!(to_string(f, 0.5, Minus, 0), "1");
1010+
assert_eq!(to_string(f, 0.5, Minus, 0), "0");
10111011
assert_eq!(to_string(f, 0.5, Minus, 1), "0.5");
10121012
assert_eq!(to_string(f, 0.5, Minus, 2), "0.50");
10131013
assert_eq!(to_string(f, 0.5, Minus, 3), "0.500");

0 commit comments

Comments
 (0)