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

Commit cf0d9a3

Browse files
committed
Adjust precision and add xfails based on new tests
1 parent d7fa2b9 commit cf0d9a3

File tree

2 files changed

+121
-13
lines changed

2 files changed

+121
-13
lines changed

crates/libm-test/src/precision.rs

Lines changed: 115 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -370,59 +370,116 @@ fn maybe_check_nan_bits<F: Float>(actual: F, expected: F, ctx: &CheckCtx) -> Opt
370370
impl MaybeOverride<(f16, f16)> for SpecialCase {
371371
fn check_float<F: Float>(
372372
input: (f16, f16),
373-
_actual: F,
373+
actual: F,
374374
expected: F,
375375
_ulp: &mut u32,
376376
ctx: &CheckCtx,
377377
) -> Option<TestResult> {
378-
maybe_skip_binop_nan(input, expected, ctx)
378+
binop_common(input, actual, expected, ctx)
379379
}
380380
}
381381

382382
impl MaybeOverride<(f32, f32)> for SpecialCase {
383383
fn check_float<F: Float>(
384384
input: (f32, f32),
385-
_actual: F,
385+
actual: F,
386386
expected: F,
387387
_ulp: &mut u32,
388388
ctx: &CheckCtx,
389389
) -> Option<TestResult> {
390-
maybe_skip_binop_nan(input, expected, ctx)
390+
if ctx.base_name == BaseName::Fmin
391+
&& input.0.biteq(f32::NEG_ZERO)
392+
&& input.1.biteq(f32::ZERO)
393+
&& expected.biteq(F::NEG_ZERO)
394+
&& actual.biteq(F::ZERO)
395+
{
396+
return XFAIL;
397+
}
398+
399+
binop_common(input, actual, expected, ctx)
391400
}
392401
}
393402

394403
impl MaybeOverride<(f64, f64)> for SpecialCase {
395404
fn check_float<F: Float>(
396405
input: (f64, f64),
397-
_actual: F,
406+
actual: F,
398407
expected: F,
399408
_ulp: &mut u32,
400409
ctx: &CheckCtx,
401410
) -> Option<TestResult> {
402-
maybe_skip_binop_nan(input, expected, ctx)
411+
if ctx.base_name == BaseName::Fmin
412+
&& input.0.biteq(f64::NEG_ZERO)
413+
&& input.1.biteq(f64::ZERO)
414+
&& expected.biteq(F::ZERO)
415+
&& actual.biteq(F::NEG_ZERO)
416+
{
417+
return XFAIL;
418+
}
419+
420+
binop_common(input, actual, expected, ctx)
421+
}
422+
423+
fn check_int<I: Int>(
424+
_input: (f64, f64),
425+
actual: I,
426+
expected: I,
427+
ctx: &CheckCtx,
428+
) -> Option<TestResult> {
429+
// FIXME: Our MPFR implementation disagrees with musl and may need to be updated.
430+
if ctx.basis == CheckBasis::Mpfr
431+
&& ctx.base_name == BaseName::Remquo
432+
&& expected == I::MIN
433+
&& actual == I::ZERO
434+
{
435+
return XFAIL;
436+
}
437+
438+
None
403439
}
404440
}
405441

406442
#[cfg(f128_enabled)]
407443
impl MaybeOverride<(f128, f128)> for SpecialCase {
408444
fn check_float<F: Float>(
409445
input: (f128, f128),
410-
_actual: F,
446+
actual: F,
411447
expected: F,
412448
_ulp: &mut u32,
413449
ctx: &CheckCtx,
414450
) -> Option<TestResult> {
415-
maybe_skip_binop_nan(input, expected, ctx)
451+
binop_common(input, actual, expected, ctx)
416452
}
417453
}
418454

419-
/// Musl propagates NaNs if one is provided as the input, but we return the other input.
420455
// F1 and F2 are always the same type, this is just to please generics
421-
fn maybe_skip_binop_nan<F1: Float, F2: Float>(
456+
fn binop_common<F1: Float, F2: Float>(
422457
input: (F1, F1),
458+
actual: F2,
423459
expected: F2,
424460
ctx: &CheckCtx,
425461
) -> Option<TestResult> {
462+
/* FIXME(#439): we do not compare signed zeros */
463+
464+
if ctx.base_name == BaseName::Fmin
465+
&& input.0.biteq(F1::NEG_ZERO)
466+
&& input.1.biteq(F1::ZERO)
467+
&& expected.biteq(F2::NEG_ZERO)
468+
&& actual.biteq(F2::ZERO)
469+
{
470+
return XFAIL;
471+
}
472+
473+
if ctx.base_name == BaseName::Fmax
474+
&& input.0.biteq(F1::NEG_ZERO)
475+
&& input.1.biteq(F1::ZERO)
476+
&& expected.biteq(F2::ZERO)
477+
&& actual.biteq(F2::NEG_ZERO)
478+
{
479+
return XFAIL;
480+
}
481+
482+
// Musl propagates NaNs if one is provided as the input, but we return the other input.
426483
match (&ctx.basis, ctx.base_name) {
427484
(Musl, BaseName::Fmin | BaseName::Fmax)
428485
if (input.0.is_nan() || input.1.is_nan()) && expected.is_nan() =>
@@ -502,7 +559,53 @@ fn bessel_prec_dropoff<F: Float>(
502559
None
503560
}
504561

505-
impl MaybeOverride<(f32, f32, f32)> for SpecialCase {}
506-
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {}
507562
impl MaybeOverride<(f32, i32)> for SpecialCase {}
508563
impl MaybeOverride<(f64, i32)> for SpecialCase {}
564+
565+
impl MaybeOverride<(f32, f32, f32)> for SpecialCase {
566+
fn check_float<F: Float>(
567+
input: (f32, f32, f32),
568+
actual: F,
569+
expected: F,
570+
_ulp: &mut u32,
571+
ctx: &CheckCtx,
572+
) -> Option<TestResult> {
573+
ternop_common(input, actual, expected, ctx)
574+
}
575+
}
576+
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {
577+
fn check_float<F: Float>(
578+
input: (f64, f64, f64),
579+
actual: F,
580+
expected: F,
581+
_ulp: &mut u32,
582+
ctx: &CheckCtx,
583+
) -> Option<TestResult> {
584+
ternop_common(input, actual, expected, ctx)
585+
}
586+
}
587+
588+
// F1 and F2 are always the same type, this is just to please generics
589+
fn ternop_common<F1: Float, F2: Float>(
590+
input: (F1, F1, F1),
591+
actual: F2,
592+
expected: F2,
593+
ctx: &CheckCtx,
594+
) -> Option<TestResult> {
595+
// FIXME(fma): 754-2020 says "When the exact result of (a × b) + c is non-zero yet the result
596+
// of fusedMultiplyAdd is zero because of rounding, the zero result takes the sign of the
597+
// exact result". Our implementation returns the wrong sign:
598+
// fma(5e-324, -5e-324, 0.0) = 0.0 (should be -0.0)
599+
if ctx.base_name == BaseName::Fma
600+
&& (input.0.is_sign_negative() ^ input.1.is_sign_negative())
601+
&& input.0 != F1::ZERO
602+
&& input.1 != F1::ZERO
603+
&& input.2.biteq(F1::ZERO)
604+
&& expected.biteq(F2::NEG_ZERO)
605+
&& actual.biteq(F2::ZERO)
606+
{
607+
return XFAIL;
608+
}
609+
610+
None
611+
}

src/math/support/float_traits.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,14 @@ pub trait Float:
9393
/// Returns true if the value is +inf or -inf.
9494
fn is_infinite(self) -> bool;
9595

96-
/// Returns true if the sign is negative.
96+
/// Returns true if the sign is negative. Extracts the sign bit regardless of zero or NaN.
9797
fn is_sign_negative(self) -> bool;
9898

99+
/// Returns true if the sign is positive. Extracts the sign bit regardless of zero or NaN.
100+
fn is_sign_positive(self) -> bool {
101+
!self.is_sign_negative()
102+
}
103+
99104
/// Returns if `self` is subnormal
100105
fn is_subnormal(self) -> bool {
101106
(self.to_bits() & Self::EXP_MASK) == Self::Int::ZERO

0 commit comments

Comments
 (0)