fmaximum,fminimum: Fix incorrect result and add tests
After adding tests, the current implementation for fminimum fails when
provided a negative zero and NaN as inputs:
---- math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f64 stdout ----
thread 'math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f64' panicked at libm/src/math/fminimum_fmaximum_num.rs:240:13:
fmaximum_num(-0x0p+0, NaN)
l: NaN (0x7ff8000000000000)
r: -0.0 (0x8000000000000000)
---- math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f32 stdout ----
thread 'math::fminimum_fmaximum_num::tests::fmaximum_num_spec_tests_f32' panicked at libm/src/math/fminimum_fmaximum_num.rs:240:13:
fmaximum_num(-0x0p+0, NaN)
l: NaN (0x7fc00000)
r: -0.0 (0x80000000)
Add more thorough spec tests for these functions and correct the
implementations.
Canonicalization is also moved to a trait method to centralize
documentation about what it does and doesn't do.
This commit is contained in:
@@ -82,22 +82,77 @@ mod tests {
|
||||
fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ONE, F::INFINITY, F::ONE),
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
(F::INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -125,22 +180,77 @@ mod tests {
|
||||
fn fmax_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::ONE),
|
||||
(F::ONE, F::INFINITY, F::INFINITY),
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ONE, F::INFINITY),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between zeros and NaNs does not matter
|
||||
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
|
||||
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -74,24 +74,77 @@ mod tests {
|
||||
fn fminimum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ZERO, F::INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NAN),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ONE, F::INFINITY, F::ONE),
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::NAN),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NAN),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
(F::INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::NAN),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NAN),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::NAN, F::NEG_ZERO, F::NAN),
|
||||
(F::NAN, F::ONE, F::NAN),
|
||||
(F::NAN, F::NEG_ONE, F::NAN),
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fminimum({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -119,24 +172,77 @@ mod tests {
|
||||
fn fmaximum_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::ZERO),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::NAN),
|
||||
(F::NEG_ZERO, F::ZERO, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NAN),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::ONE),
|
||||
(F::ONE, F::INFINITY, F::INFINITY),
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::NAN),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NAN),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ONE, F::INFINITY),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::NAN),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NAN),
|
||||
(F::NAN, F::ZERO, F::NAN),
|
||||
(F::NAN, F::NEG_ZERO, F::NAN),
|
||||
(F::NAN, F::ONE, F::NAN),
|
||||
(F::NAN, F::NEG_ONE, F::NAN),
|
||||
(F::NAN, F::INFINITY, F::NAN),
|
||||
(F::NAN, F::NEG_INFINITY, F::NAN),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmaximum({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ZERO, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_ONE, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_INFINITY, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ZERO).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_ONE).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_INFINITY).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -74,24 +74,77 @@ mod tests {
|
||||
fn fminimum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ZERO, F::ONE, F::ZERO),
|
||||
(F::ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ZERO, F::INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::ONE, F::INFINITY, F::ONE),
|
||||
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::INFINITY, F::ONE, F::ONE),
|
||||
(F::INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fminimum_num({}, {})", Hexf(x), Hexf(y));
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fminimum_num({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -119,24 +172,77 @@ mod tests {
|
||||
fn fmaximum_num_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
|
||||
let cases = [
|
||||
(F::ZERO, F::ZERO, F::ZERO),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::ZERO, F::NEG_ZERO, F::ZERO),
|
||||
(F::ZERO, F::ONE, F::ONE),
|
||||
(F::ZERO, F::NEG_ONE, F::ZERO),
|
||||
(F::ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::ZERO, F::NEG_INFINITY, F::ZERO),
|
||||
(F::ZERO, F::NAN, F::ZERO),
|
||||
(F::ZERO, F::NEG_NAN, F::ZERO),
|
||||
(F::NEG_ZERO, F::ZERO, F::ZERO),
|
||||
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::ONE, F::ONE),
|
||||
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
|
||||
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
|
||||
(F::ONE, F::ZERO, F::ONE),
|
||||
(F::ONE, F::NEG_ZERO, F::ONE),
|
||||
(F::ONE, F::ONE, F::ONE),
|
||||
(F::ONE, F::NEG_ONE, F::ONE),
|
||||
(F::ONE, F::INFINITY, F::INFINITY),
|
||||
(F::ONE, F::NEG_INFINITY, F::ONE),
|
||||
(F::ONE, F::NAN, F::ONE),
|
||||
(F::ONE, F::NEG_NAN, F::ONE),
|
||||
(F::NEG_ONE, F::ZERO, F::ZERO),
|
||||
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_ONE, F::ONE, F::ONE),
|
||||
(F::NEG_ONE, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NAN, F::NEG_ONE),
|
||||
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
|
||||
(F::INFINITY, F::ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
|
||||
(F::INFINITY, F::ONE, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_ONE, F::INFINITY),
|
||||
(F::INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
|
||||
(F::INFINITY, F::NAN, F::INFINITY),
|
||||
(F::INFINITY, F::NEG_NAN, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::ZERO, F::ZERO),
|
||||
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_INFINITY, F::ONE, F::ONE),
|
||||
(F::NEG_INFINITY, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_INFINITY, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
|
||||
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
|
||||
(F::NAN, F::ZERO, F::ZERO),
|
||||
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NAN, F::ONE, F::ONE),
|
||||
(F::NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
(F::NAN, F::NAN, F::NAN),
|
||||
(F::NEG_NAN, F::ZERO, F::ZERO),
|
||||
(F::NEG_NAN, F::NEG_ZERO, F::NEG_ZERO),
|
||||
(F::NEG_NAN, F::ONE, F::ONE),
|
||||
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
|
||||
(F::NEG_NAN, F::INFINITY, F::INFINITY),
|
||||
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
|
||||
];
|
||||
|
||||
for (x, y, res) in cases {
|
||||
let val = f(x, y);
|
||||
assert_biteq!(val, res, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
|
||||
for (x, y, expected) in cases {
|
||||
let actual = f(x, y);
|
||||
assert_biteq!(actual, expected, "fmaximum_num({}, {})", Hexf(x), Hexf(y));
|
||||
}
|
||||
|
||||
// Ordering between NaNs does not matter
|
||||
assert!(f(F::NAN, F::NEG_NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NAN).is_nan());
|
||||
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -19,6 +19,5 @@ use crate::support::Float;
|
||||
#[inline]
|
||||
pub fn fmax<F: Float>(x: F, y: F) -> F {
|
||||
let res = if x.is_nan() || x < y { y } else { x };
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! Per the spec, returns the canonicalized result of:
|
||||
//! - `x` if `x > y`
|
||||
//! - `y` if `y > x`
|
||||
//! - +0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -23,6 +23,5 @@ pub fn fmaximum<F: Float>(x: F, y: F) -> F {
|
||||
y
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
//! Per the spec, returns:
|
||||
//! - `x` if `x > y`
|
||||
//! - `y` if `y > x`
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//! - +0.0 if x and y are zero with opposite signs
|
||||
//! - Either `x` or `y` if `x == y` and the signs are the same
|
||||
//! - qNaN if either operand is a NaN
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -15,12 +15,15 @@ use crate::support::Float;
|
||||
|
||||
#[inline]
|
||||
pub fn fmaximum_num<F: Float>(x: F, y: F) -> F {
|
||||
let res = if x.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) {
|
||||
y
|
||||
} else {
|
||||
let res = if x > y || y.is_nan() {
|
||||
x
|
||||
} else if y > x || x.is_nan() {
|
||||
y
|
||||
} else if x.is_sign_positive() {
|
||||
x
|
||||
} else {
|
||||
y
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -19,6 +19,5 @@ use crate::support::Float;
|
||||
#[inline]
|
||||
pub fn fmin<F: Float>(x: F, y: F) -> F {
|
||||
let res = if y.is_nan() || x < y { x } else { y };
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
//! Per the spec, returns the canonicalized result of:
|
||||
//! - `x` if `x < y`
|
||||
//! - `y` if `y < x`
|
||||
//! - -0.0 if x and y are zero with opposite signs
|
||||
//! - qNaN if either operation is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -23,6 +23,5 @@ pub fn fminimum<F: Float>(x: F, y: F) -> F {
|
||||
y
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
//! Per the spec, returns:
|
||||
//! - `x` if `x < y`
|
||||
//! - `y` if `y < x`
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - Logic following +0.0 > -0.0
|
||||
//! - -0.0 if x and y are zero with opposite signs
|
||||
//! - Either `x` or `y` if `x == y` and the signs are the same
|
||||
//! - qNaN if either operand is a NaN
|
||||
//! - Non-NaN if one operand is NaN
|
||||
//! - qNaN if both operands are NaNx
|
||||
//!
|
||||
//! Excluded from our implementation is sNaN handling.
|
||||
|
||||
@@ -15,12 +15,15 @@ use crate::support::Float;
|
||||
|
||||
#[inline]
|
||||
pub fn fminimum_num<F: Float>(x: F, y: F) -> F {
|
||||
let res = if y.is_nan() || x < y || (x.biteq(F::NEG_ZERO) && y.is_sign_positive()) {
|
||||
x
|
||||
} else {
|
||||
let res = if x > y || x.is_nan() {
|
||||
y
|
||||
} else if y > x || y.is_nan() {
|
||||
x
|
||||
} else if x.is_sign_positive() {
|
||||
y
|
||||
} else {
|
||||
x
|
||||
};
|
||||
|
||||
// Canonicalize
|
||||
res * F::ONE
|
||||
res.canonicalize()
|
||||
}
|
||||
|
||||
@@ -190,6 +190,15 @@ pub trait Float:
|
||||
Self::ONE.copysign(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a best-effort attempt to canonicalize the number. Note that this is allowed
|
||||
/// to be a nop and does not always quiet sNaNs.
|
||||
fn canonicalize(self) -> Self {
|
||||
// FIXME: LLVM often removes this. We should determine whether we can remove the operation,
|
||||
// or switch to something based on `llvm.canonicalize` (which has crashes,
|
||||
// <https://github.com/llvm/llvm-project/issues/32650>).
|
||||
self * Self::ONE
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the associated `Int` type from a float (helper to avoid ambiguous associated types).
|
||||
|
||||
@@ -143,10 +143,12 @@ macro_rules! assert_biteq {
|
||||
let bits = $crate::support::Int::leading_zeros(l.to_bits() - l.to_bits());
|
||||
assert!(
|
||||
$crate::support::Float::biteq(l, r),
|
||||
"{}\nl: {l:?} ({lb:#0width$x})\nr: {r:?} ({rb:#0width$x})",
|
||||
"{}\nl: {l:?} ({lb:#0width$x} {lh})\nr: {r:?} ({rb:#0width$x} {rh})",
|
||||
format_args!($($tt)*),
|
||||
lb = l.to_bits(),
|
||||
lh = $crate::support::Hexf(l),
|
||||
rb = r.to_bits(),
|
||||
rh = $crate::support::Hexf(r),
|
||||
width = ((bits / 4) + 2) as usize,
|
||||
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user