Skip to content

Commit caa16fd

Browse files
committed
Add a test for incorrect rounding to even
1 parent 8d4f400 commit caa16fd

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

src/etc/test-float-parse/src/validate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ impl<F: Float> FloatRes<F> {
246246
}
247247
}
248248

249-
/// Decompose a float into its integral components.
249+
/// Decompose a float into its integral components. This includes the implicit bit.
250250
///
251251
/// If `allow_nan` is `false`, panic if `NaN` values are reached.
252252
fn decode<F: Float>(f: F, allow_nan: bool) -> FloatRes<F> {

src/etc/test-float-parse/src/validate/tests.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fn test_validate() {
6666
}
6767

6868
#[test]
69-
fn test_check() {
69+
fn test_validate_real() {
7070
// Most of the arbitrary values come from checking against <http://weitz.de/ieee/>.
7171
let r = &BigRational::from_float(10.0).unwrap();
7272
FloatRes::<f32>::validate_real(r.clone(), 10, 0).unwrap();
@@ -88,6 +88,61 @@ fn test_check() {
8888
FloatRes::<f32>::validate_real(r.clone(), -0b100110100101001000101100, -13).unwrap_err();
8989
}
9090

91+
#[test]
92+
#[allow(unused)]
93+
fn test_validate_real_rounding() {
94+
// Check that we catch when values don't round to even.
95+
96+
// For f32, the cutoff between 1.0 and the next value up (1.0000001) is
97+
// 1.000000059604644775390625. Anything below it should round down, anything above it should
98+
// round up, and the value itself should round _down_ because `1.0` has an even significand but
99+
// 1.0000001 is odd.
100+
let v1_low_down = Rational::parse("1.00000005960464477539062499999").expect_finite();
101+
let v1_mid_down = Rational::parse("1.000000059604644775390625").expect_finite();
102+
let v1_high_up = Rational::parse("1.00000005960464477539062500001").expect_finite();
103+
104+
let exp = -(f32::MAN_BITS as i32);
105+
let v1_down_sig = 1 << f32::MAN_BITS;
106+
let v1_up_sig = (1 << f32::MAN_BITS) | 0b1;
107+
108+
FloatRes::<f32>::validate_real(v1_low_down.clone(), v1_down_sig, exp).unwrap();
109+
FloatRes::<f32>::validate_real(v1_mid_down.clone(), v1_down_sig, exp).unwrap();
110+
FloatRes::<f32>::validate_real(v1_high_up.clone(), v1_up_sig, exp).unwrap();
111+
FloatRes::<f32>::validate_real(-v1_low_down.clone(), -v1_down_sig, exp).unwrap();
112+
FloatRes::<f32>::validate_real(-v1_mid_down.clone(), -v1_down_sig, exp).unwrap();
113+
FloatRes::<f32>::validate_real(-v1_high_up.clone(), -v1_up_sig, exp).unwrap();
114+
115+
// 1.000000178813934326171875 is between 1.0000001 and the next value up, 1.0000002. The middle
116+
// value here should round _up_ since 1.0000002 has an even mantissa.
117+
let v2_low_down = Rational::parse("1.00000017881393432617187499999").expect_finite();
118+
let v2_mid_up = Rational::parse("1.000000178813934326171875").expect_finite();
119+
let v2_high_up = Rational::parse("1.00000017881393432617187500001").expect_finite();
120+
121+
let v2_down_sig = v1_up_sig;
122+
let v2_up_sig = (1 << f32::MAN_BITS) | 0b10;
123+
124+
FloatRes::<f32>::validate_real(v2_low_down.clone(), v2_down_sig, exp).unwrap();
125+
FloatRes::<f32>::validate_real(v2_mid_up.clone(), v2_up_sig, exp).unwrap();
126+
FloatRes::<f32>::validate_real(v2_high_up.clone(), v2_up_sig, exp).unwrap();
127+
FloatRes::<f32>::validate_real(-v2_low_down.clone(), -v2_down_sig, exp).unwrap();
128+
FloatRes::<f32>::validate_real(-v2_mid_up.clone(), -v2_up_sig, exp).unwrap();
129+
FloatRes::<f32>::validate_real(-v2_high_up.clone(), -v2_up_sig, exp).unwrap();
130+
131+
// Rounding the wrong direction should error
132+
for res in [
133+
FloatRes::<f32>::validate_real(v1_mid_down.clone(), v1_up_sig, exp),
134+
FloatRes::<f32>::validate_real(v2_mid_up.clone(), v2_down_sig, exp),
135+
FloatRes::<f32>::validate_real(-v1_mid_down.clone(), -v1_up_sig, exp),
136+
FloatRes::<f32>::validate_real(-v2_mid_up.clone(), -v2_down_sig, exp),
137+
] {
138+
let e = res.unwrap_err();
139+
let CheckFailure::InvalidReal { incorrect_midpoint_rounding: true, .. } = e else {
140+
panic!("{e:?}");
141+
};
142+
}
143+
}
144+
145+
/// Just a quick check that the constants are what we expect.
91146
#[test]
92147
fn check_constants() {
93148
assert_eq!(f32::constants().max.to_f32().unwrap(), f32::MAX);

0 commit comments

Comments
 (0)