libm-test: Fix unintentional skips in binop_common

`binop_common` emits a `SKIP` that is intended to apply only to
`copysign`, but is instead applying to all binary operators. Correct the
general case but leave the currently-failing `maximum_num` tests as a
FIXME, to be resolved separately in [1].

Also simplify skip logic and NaN checking, and add a few more `copysign`
checks.

[1]: https://github.com/rust-lang/compiler-builtins/pull/939
This commit is contained in:
Trevor Gross 2025-06-02 20:20:23 +00:00
parent ba7cdb6814
commit 5778643174
4 changed files with 34 additions and 12 deletions

View file

@ -51,6 +51,7 @@ where
// Check some special values that aren't included in the above ranges
values.push(Op::FTy::NAN);
values.push(Op::FTy::NEG_NAN);
values.extend(Op::FTy::consts().iter());
// Check around the maximum subnormal value

View file

@ -444,13 +444,18 @@ fn binop_common<F1: Float, F2: Float>(
expected: F2,
ctx: &CheckCtx,
) -> CheckAction {
// MPFR only has one NaN bitpattern; allow the default `.is_nan()` checks to validate. Skip if
// the first input (magnitude source) is NaN and the output is also a NaN, or if the second
// input (sign source) is NaN.
if ctx.basis == CheckBasis::Mpfr
// MPFR only has one NaN bitpattern; skip tests in cases where the first argument would take
// the sign of a NaN second argument. The default NaN checks cover other cases.
if ctx.base_name == BaseName::Copysign && ctx.basis == CheckBasis::Mpfr && input.1.is_nan() {
return SKIP;
}
// FIXME(#939): this should not be skipped, there is a bug in our implementationi.
if ctx.base_name == BaseName::FmaximumNum
&& ctx.basis == CheckBasis::Mpfr
&& ((input.0.is_nan() && actual.is_nan() && expected.is_nan()) || input.1.is_nan())
{
return SKIP;
return XFAIL_NOCHECK;
}
/* FIXME(#439): our fmin and fmax do not compare signed zeros */

View file

@ -312,12 +312,9 @@ where
let mut inner = || -> TestResult {
let mut allowed_ulp = ctx.ulp;
// Forbid overrides if the items came from an explicit list, as long as we are checking
// against either MPFR or the result itself.
let require_biteq = ctx.gen_kind == GeneratorKind::List && ctx.basis != CheckBasis::Musl;
match SpecialCase::check_float(input, actual, expected, ctx) {
_ if require_biteq => (),
// Forbid overrides if the items came from an explicit list
_ if ctx.gen_kind == GeneratorKind::List => (),
CheckAction::AssertSuccess => (),
CheckAction::AssertFailure(msg) => assert_failure_msg = Some(msg),
CheckAction::Custom(res) => return res,
@ -327,9 +324,20 @@ where
// Check when both are NaNs
if actual.is_nan() && expected.is_nan() {
if require_biteq && ctx.basis == CheckBasis::None {
// Don't assert NaN bitwise equality if:
//
// * Testing against MPFR (there is a single NaN representation)
// * Testing against Musl except for explicit tests (Musl does some NaN quieting)
//
// In these cases, just the check that actual and expected are both NaNs is
// sufficient.
let skip_nan_biteq = ctx.basis == CheckBasis::Mpfr
|| (ctx.basis == CheckBasis::Musl && ctx.gen_kind != GeneratorKind::List);
if !skip_nan_biteq {
ensure!(actual.biteq(expected), "mismatched NaN bitpatterns");
}
// By default, NaNs have nothing special to check.
return Ok(());
} else if actual.is_nan() || expected.is_nan() {

View file

@ -59,9 +59,17 @@ mod tests {
// Not required but we expect it
assert_biteq!(f(F::NAN, F::NAN), F::NAN);
assert_biteq!(f(F::NEG_NAN, F::NAN), F::NAN);
assert_biteq!(f(F::NAN, F::ONE), F::NAN);
assert_biteq!(f(F::NAN, F::NEG_ONE), F::NEG_NAN);
assert_biteq!(f(F::NAN, F::NEG_NAN), F::NEG_NAN);
assert_biteq!(f(F::NEG_NAN, F::NAN), F::NAN);
assert_biteq!(f(F::NEG_NAN, F::ONE), F::NAN);
assert_biteq!(f(F::NEG_NAN, F::NEG_ONE), F::NEG_NAN);
assert_biteq!(f(F::NEG_NAN, F::NEG_NAN), F::NEG_NAN);
assert_biteq!(f(F::ONE, F::NAN), F::ONE);
assert_biteq!(f(F::ONE, F::NEG_NAN), F::NEG_ONE);
assert_biteq!(f(F::NEG_ONE, F::NAN), F::ONE);
assert_biteq!(f(F::NEG_ONE, F::NEG_NAN), F::NEG_ONE);
}
#[test]