Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit d10109a

Browse files
add tests to check that the operations behave like the Ieee and C standard specify
1 parent 4559163 commit d10109a

File tree

1 file changed

+59
-8
lines changed

1 file changed

+59
-8
lines changed

src/tools/miri/tests/pass/float.rs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ macro_rules! assert_approx_eq {
3838
}};
3939

4040
($a:expr, $b: expr) => {
41-
// accept up to 64ULP (16ULP for host floats and 16ULP for miri artificial error and 32 for any rounding errors)
42-
assert_approx_eq!($a, $b, 64);
41+
// accept up to 52ULP (16ULP for host floats, 4ULP for miri artificial error and 32 for any rounding errors)
42+
assert_approx_eq!($a, $b, 52);
4343
};
4444
}
4545

@@ -1006,17 +1006,52 @@ pub fn libm() {
10061006
assert_approx_eq!(25f32.powf(-2f32), 0.0016f32);
10071007
assert_approx_eq!(400f64.powf(0.5f64), 20f64);
10081008

1009+
// Some inputs to powf and powi result in fixed outputs
1010+
// and thus must be exactly equal to that value
1011+
// TODO: How to test NaN inputs? f*::NAN is not guaranteed
1012+
// to be any specific bit pattern (in std).
1013+
assert_eq!(1f32.powf(10.0), 1f32);
1014+
assert_eq!(1f64.powf(100.0), 1f64);
1015+
assert_eq!(1f32.powf(f32::INFINITY), 1f32);
1016+
assert_eq!(1f64.powf(f64::INFINITY), 1f64);
1017+
1018+
assert_eq!((-1f32).powf(f32::INFINITY), 1f32);
1019+
assert_eq!((-1f32).powf(f32::NEG_INFINITY), 1f32);
1020+
assert_eq!((-1f64).powf(f64::INFINITY), 1f64);
1021+
assert_eq!((-1f64).powf(f64::NEG_INFINITY), 1f64);
1022+
1023+
assert_eq!(42f32.powf(0.0), 1f32);
1024+
assert_eq!(42f32.powf(-0.0), 1f32);
1025+
assert_eq!(42f64.powf(0.0), 1f64);
1026+
assert_eq!(42f64.powf(-0.0), 1f64);
1027+
1028+
assert_eq!(0f32.powi(10), 0f32);
1029+
assert_eq!(0f64.powi(100), 0f64);
1030+
assert_eq!(0f32.powi(9), 0f32);
1031+
assert_eq!(0f64.powi(99), 0f64);
1032+
1033+
assert_eq!((-0f32).powi(10), 0f32);
1034+
assert_eq!((-0f64).powi(100), 0f64);
1035+
assert_eq!((-0f32).powi(9), -0f32);
1036+
assert_eq!((-0f64).powi(99), -0f64);
1037+
10091038
assert_approx_eq!(1f32.exp(), f32::consts::E);
10101039
assert_approx_eq!(1f64.exp(), f64::consts::E);
1040+
assert_eq!(0f32.exp(), 1f32);
1041+
assert_eq!(0f64.exp(), 1f64);
10111042

10121043
assert_approx_eq!(1f32.exp_m1(), f32::consts::E - 1.0);
10131044
assert_approx_eq!(1f64.exp_m1(), f64::consts::E - 1.0);
10141045

10151046
assert_approx_eq!(10f32.exp2(), 1024f32);
10161047
assert_approx_eq!(50f64.exp2(), 1125899906842624f64);
1048+
assert_eq!(0f32.exp2(), 1f32);
1049+
assert_eq!(0f64.exp2(), 1f64);
10171050

10181051
assert_approx_eq!(f32::consts::E.ln(), 1f32);
1019-
assert_approx_eq!(1f64.ln(), 0f64);
1052+
assert_approx_eq!(f64::consts::E.ln(), 1f64);
1053+
assert_eq!(1f32.ln(), 0f32);
1054+
assert_eq!(1f64.ln(), 0f64);
10201055

10211056
assert_approx_eq!(0f32.ln_1p(), 0f32);
10221057
assert_approx_eq!(0f64.ln_1p(), 0f64);
@@ -1045,7 +1080,8 @@ pub fn libm() {
10451080

10461081
// Trigonometric functions.
10471082

1048-
assert_approx_eq!(0f32.sin(), 0f32);
1083+
assert_eq!(0f32.sin(), 0f32);
1084+
assert_eq!(0f64.sin(), 0f64);
10491085
assert_approx_eq!((f64::consts::PI / 2f64).sin(), 1f64);
10501086
assert_approx_eq!(f32::consts::FRAC_PI_6.sin(), 0.5);
10511087
assert_approx_eq!(f64::consts::FRAC_PI_6.sin(), 0.5);
@@ -1057,7 +1093,23 @@ pub fn libm() {
10571093
assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
10581094
assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
10591095

1060-
assert_approx_eq!(0f32.cos(), 1f32);
1096+
// from #4207
1097+
// TODO: should this be the behaviour? I haven't found anything in the IEEE Standard
1098+
let halve_pi_single = std::f32::consts::FRAC_PI_2;
1099+
let halve_pi_double = std::f64::consts::FRAC_PI_2;
1100+
let pi_single = std::f32::consts::PI;
1101+
let pi_double = std::f64::consts::PI;
1102+
for _ in 0..64 {
1103+
// sin() should be clamped to [-1, 1] so asin() can never return NaN
1104+
assert!(!halve_pi_single.sin().asin().is_nan());
1105+
assert!(!halve_pi_double.sin().asin().is_nan());
1106+
// sin() should be clamped to [-1, 1] so acos() can never return NaN
1107+
assert!(!pi_single.cos().acos().is_nan());
1108+
assert!(!pi_double.cos().acos().is_nan());
1109+
}
1110+
1111+
assert_eq!(0f32.cos(), 1f32);
1112+
assert_eq!(0f64.cos(), 1f64);
10611113
assert_approx_eq!((f64::consts::PI * 2f64).cos(), 1f64);
10621114
assert_approx_eq!(f32::consts::FRAC_PI_3.cos(), 0.5);
10631115
assert_approx_eq!(f64::consts::FRAC_PI_3.cos(), 0.5);
@@ -1281,7 +1333,6 @@ fn test_non_determinism() {
12811333
/// Ensure that the operation is non-deterministic
12821334
#[track_caller]
12831335
fn ensure_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
1284-
12851336
let rounds = 16;
12861337
let first = f();
12871338
for _ in 1..rounds {
@@ -1328,7 +1379,7 @@ fn test_non_determinism() {
13281379
ensure_nondet(|| 27.0f32.cbrt());
13291380
ensure_nondet(|| 3.0f32.hypot(4.0f32));
13301381
ensure_nondet(|| 1f32.sin());
1331-
ensure_nondet(|| 0f32.cos());
1382+
ensure_nondet(|| 3.1f32.cos());
13321383
// On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version,
13331384
// which means the little rounding errors Miri introduces are discard by the cast down to `f32`.
13341385
// Just skip the test for them.
@@ -1362,7 +1413,7 @@ fn test_non_determinism() {
13621413
ensure_nondet(|| 27.0f64.cbrt());
13631414
ensure_nondet(|| 3.0f64.hypot(4.0f64));
13641415
ensure_nondet(|| 1f64.sin());
1365-
ensure_nondet(|| 0f64.cos());
1416+
ensure_nondet(|| 3.1f64.cos());
13661417
ensure_nondet(|| 1.0f64.tan());
13671418
ensure_nondet(|| 1.0f64.asin());
13681419
ensure_nondet(|| 5.0f64.acos());

0 commit comments

Comments
 (0)