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

Commit 7028a91

Browse files
committed
Adjust precision and add xfails based on new tests
1 parent f77f182 commit 7028a91

File tree

2 files changed

+143
-13
lines changed

2 files changed

+143
-13
lines changed

crates/libm-test/src/precision.rs

Lines changed: 137 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ pub fn default_ulp(ctx: &CheckCtx) -> u32 {
102102
}
103103
}
104104

105+
if cfg!(target_arch = "x86") {
106+
match ctx.fn_ident {
107+
// Input `fma(0.999999999999999, 1.0000000000000013, 0.0) = 1.0000000000000002` is
108+
// incorrect on i586 and i686.
109+
Id::Fma => ulp = 1,
110+
_ => (),
111+
}
112+
}
113+
105114
// In some cases, our implementation is less accurate than musl on i586.
106115
if cfg!(x86_no_sse) {
107116
match ctx.fn_ident {
@@ -370,59 +379,129 @@ fn maybe_check_nan_bits<F: Float>(actual: F, expected: F, ctx: &CheckCtx) -> Opt
370379
impl MaybeOverride<(f16, f16)> for SpecialCase {
371380
fn check_float<F: Float>(
372381
input: (f16, f16),
373-
_actual: F,
382+
actual: F,
374383
expected: F,
375384
_ulp: &mut u32,
376385
ctx: &CheckCtx,
377386
) -> Option<TestResult> {
378-
maybe_skip_binop_nan(input, expected, ctx)
387+
binop_common(input, actual, expected, ctx)
379388
}
380389
}
381390

382391
impl MaybeOverride<(f32, f32)> for SpecialCase {
383392
fn check_float<F: Float>(
384393
input: (f32, f32),
385-
_actual: F,
394+
actual: F,
386395
expected: F,
387396
_ulp: &mut u32,
388397
ctx: &CheckCtx,
389398
) -> Option<TestResult> {
390-
maybe_skip_binop_nan(input, expected, ctx)
399+
if ctx.base_name == BaseName::Fmin
400+
&& input.0.biteq(f32::NEG_ZERO)
401+
&& input.1.biteq(f32::ZERO)
402+
&& expected.biteq(F::NEG_ZERO)
403+
&& actual.biteq(F::ZERO)
404+
{
405+
return XFAIL;
406+
}
407+
408+
binop_common(input, actual, expected, ctx)
409+
}
410+
411+
fn check_int<I: Int>(
412+
_input: (f32, f32),
413+
actual: I,
414+
expected: I,
415+
ctx: &CheckCtx,
416+
) -> Option<TestResult> {
417+
remquo_common(actual, expected, ctx)
391418
}
392419
}
393420

394421
impl MaybeOverride<(f64, f64)> for SpecialCase {
395422
fn check_float<F: Float>(
396423
input: (f64, f64),
397-
_actual: F,
424+
actual: F,
398425
expected: F,
399426
_ulp: &mut u32,
400427
ctx: &CheckCtx,
401428
) -> Option<TestResult> {
402-
maybe_skip_binop_nan(input, expected, ctx)
429+
if ctx.base_name == BaseName::Fmin
430+
&& input.0.biteq(f64::NEG_ZERO)
431+
&& input.1.biteq(f64::ZERO)
432+
&& expected.biteq(F::ZERO)
433+
&& actual.biteq(F::NEG_ZERO)
434+
{
435+
return XFAIL;
436+
}
437+
438+
binop_common(input, actual, expected, ctx)
439+
}
440+
441+
fn check_int<I: Int>(
442+
_input: (f64, f64),
443+
actual: I,
444+
expected: I,
445+
ctx: &CheckCtx,
446+
) -> Option<TestResult> {
447+
remquo_common(actual, expected, ctx)
448+
}
449+
}
450+
451+
fn remquo_common<I: Int>(actual: I, expected: I, ctx: &CheckCtx) -> Option<TestResult> {
452+
// FIXME: Our MPFR implementation disagrees with musl and may need to be updated.
453+
if ctx.basis == CheckBasis::Mpfr
454+
&& ctx.base_name == BaseName::Remquo
455+
&& expected == I::MIN
456+
&& actual == I::ZERO
457+
{
458+
return XFAIL;
403459
}
460+
461+
None
404462
}
405463

406464
#[cfg(f128_enabled)]
407465
impl MaybeOverride<(f128, f128)> for SpecialCase {
408466
fn check_float<F: Float>(
409467
input: (f128, f128),
410-
_actual: F,
468+
actual: F,
411469
expected: F,
412470
_ulp: &mut u32,
413471
ctx: &CheckCtx,
414472
) -> Option<TestResult> {
415-
maybe_skip_binop_nan(input, expected, ctx)
473+
binop_common(input, actual, expected, ctx)
416474
}
417475
}
418476

419-
/// Musl propagates NaNs if one is provided as the input, but we return the other input.
420477
// F1 and F2 are always the same type, this is just to please generics
421-
fn maybe_skip_binop_nan<F1: Float, F2: Float>(
478+
fn binop_common<F1: Float, F2: Float>(
422479
input: (F1, F1),
480+
actual: F2,
423481
expected: F2,
424482
ctx: &CheckCtx,
425483
) -> Option<TestResult> {
484+
/* FIXME(#439): we do not compare signed zeros */
485+
486+
if ctx.base_name == BaseName::Fmin
487+
&& input.0.biteq(F1::NEG_ZERO)
488+
&& input.1.biteq(F1::ZERO)
489+
&& expected.biteq(F2::NEG_ZERO)
490+
&& actual.biteq(F2::ZERO)
491+
{
492+
return XFAIL;
493+
}
494+
495+
if ctx.base_name == BaseName::Fmax
496+
&& input.0.biteq(F1::NEG_ZERO)
497+
&& input.1.biteq(F1::ZERO)
498+
&& expected.biteq(F2::ZERO)
499+
&& actual.biteq(F2::NEG_ZERO)
500+
{
501+
return XFAIL;
502+
}
503+
504+
// Musl propagates NaNs if one is provided as the input, but we return the other input.
426505
match (&ctx.basis, ctx.base_name) {
427506
(Musl, BaseName::Fmin | BaseName::Fmax)
428507
if (input.0.is_nan() || input.1.is_nan()) && expected.is_nan() =>
@@ -502,7 +581,53 @@ fn bessel_prec_dropoff<F: Float>(
502581
None
503582
}
504583

505-
impl MaybeOverride<(f32, f32, f32)> for SpecialCase {}
506-
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {}
507584
impl MaybeOverride<(f32, i32)> for SpecialCase {}
508585
impl MaybeOverride<(f64, i32)> for SpecialCase {}
586+
587+
impl MaybeOverride<(f32, f32, f32)> for SpecialCase {
588+
fn check_float<F: Float>(
589+
input: (f32, f32, f32),
590+
actual: F,
591+
expected: F,
592+
_ulp: &mut u32,
593+
ctx: &CheckCtx,
594+
) -> Option<TestResult> {
595+
ternop_common(input, actual, expected, ctx)
596+
}
597+
}
598+
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {
599+
fn check_float<F: Float>(
600+
input: (f64, f64, f64),
601+
actual: F,
602+
expected: F,
603+
_ulp: &mut u32,
604+
ctx: &CheckCtx,
605+
) -> Option<TestResult> {
606+
ternop_common(input, actual, expected, ctx)
607+
}
608+
}
609+
610+
// F1 and F2 are always the same type, this is just to please generics
611+
fn ternop_common<F1: Float, F2: Float>(
612+
input: (F1, F1, F1),
613+
actual: F2,
614+
expected: F2,
615+
ctx: &CheckCtx,
616+
) -> Option<TestResult> {
617+
// FIXME(fma): 754-2020 says "When the exact result of (a × b) + c is non-zero yet the result
618+
// of fusedMultiplyAdd is zero because of rounding, the zero result takes the sign of the
619+
// exact result". Our implementation returns the wrong sign:
620+
// fma(5e-324, -5e-324, 0.0) = 0.0 (should be -0.0)
621+
if ctx.base_name == BaseName::Fma
622+
&& (input.0.is_sign_negative() ^ input.1.is_sign_negative())
623+
&& input.0 != F1::ZERO
624+
&& input.1 != F1::ZERO
625+
&& input.2.biteq(F1::ZERO)
626+
&& expected.biteq(F2::NEG_ZERO)
627+
&& actual.biteq(F2::ZERO)
628+
{
629+
return XFAIL;
630+
}
631+
632+
None
633+
}

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)