Skip to content

Update the compiler-builtins subtree #142489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jun 14, 2025
Prev Previous commit
Next Next commit
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.
  • Loading branch information
tgross35 committed Jun 13, 2025
commit 10ba25f7ce4b98ec0958ad7ce52bc2585f6fbb55
122 changes: 116 additions & 6 deletions library/compiler-builtins/libm/src/math/fmin_fmax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::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::ZERO, F::NAN, 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]
Expand Down Expand Up @@ -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::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::ZERO, F::NAN, 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]
Expand Down
126 changes: 116 additions & 10 deletions library/compiler-builtins/libm/src/math/fminimum_fmaximum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::NEG_ZERO, F::NEG_ZERO),
(F::ZERO, F::ONE, F::ZERO),
(F::ONE, F::ZERO, 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::ZERO, F::NAN, 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),
(F::ZERO, F::NEG_ZERO, F::NEG_ZERO),
(F::NEG_ZERO, F::ZERO, F::NEG_ZERO),
];

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]
Expand Down Expand Up @@ -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::NEG_ZERO, F::ZERO),
(F::ZERO, F::ONE, F::ONE),
(F::ONE, F::ZERO, 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::ZERO, F::NAN, 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),
(F::ZERO, F::NEG_ZERO, F::ZERO),
(F::NEG_ZERO, F::ZERO, F::ZERO),
];

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]
Expand Down
Loading