Skip to content

Commit

Permalink
Adjust precision and add xfails based on new tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tgross35 committed Jan 13, 2025
1 parent 577310c commit 7788a3d
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 13 deletions.
140 changes: 128 additions & 12 deletions crates/libm-test/src/precision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,59 +370,129 @@ fn maybe_check_nan_bits<F: Float>(actual: F, expected: F, ctx: &CheckCtx) -> Opt
impl MaybeOverride<(f16, f16)> for SpecialCase {
fn check_float<F: Float>(
input: (f16, f16),
_actual: F,
actual: F,
expected: F,
_ulp: &mut u32,
ctx: &CheckCtx,
) -> Option<TestResult> {
maybe_skip_binop_nan(input, expected, ctx)
binop_common(input, actual, expected, ctx)
}
}

impl MaybeOverride<(f32, f32)> for SpecialCase {
fn check_float<F: Float>(
input: (f32, f32),
_actual: F,
actual: F,
expected: F,
_ulp: &mut u32,
ctx: &CheckCtx,
) -> Option<TestResult> {
maybe_skip_binop_nan(input, expected, ctx)
if ctx.base_name == BaseName::Fmin
&& input.0.biteq(f32::NEG_ZERO)
&& input.1.biteq(f32::ZERO)
&& expected.biteq(F::NEG_ZERO)
&& actual.biteq(F::ZERO)
{
return XFAIL;
}

binop_common(input, actual, expected, ctx)
}

fn check_int<I: Int>(
_input: (f32, f32),
actual: I,
expected: I,
ctx: &CheckCtx,
) -> Option<TestResult> {
remquo_common(actual, expected, ctx)
}
}

impl MaybeOverride<(f64, f64)> for SpecialCase {
fn check_float<F: Float>(
input: (f64, f64),
_actual: F,
actual: F,
expected: F,
_ulp: &mut u32,
ctx: &CheckCtx,
) -> Option<TestResult> {
maybe_skip_binop_nan(input, expected, ctx)
if ctx.base_name == BaseName::Fmin
&& input.0.biteq(f64::NEG_ZERO)
&& input.1.biteq(f64::ZERO)
&& expected.biteq(F::ZERO)
&& actual.biteq(F::NEG_ZERO)
{
return XFAIL;
}

binop_common(input, actual, expected, ctx)
}

fn check_int<I: Int>(
_input: (f64, f64),
actual: I,
expected: I,
ctx: &CheckCtx,
) -> Option<TestResult> {
remquo_common(actual, expected, ctx)
}
}

fn remquo_common<I: Int>(actual: I, expected: I, ctx: &CheckCtx) -> Option<TestResult> {
// FIXME: Our MPFR implementation disagrees with musl and may need to be updated.
if ctx.basis == CheckBasis::Mpfr
&& ctx.base_name == BaseName::Remquo
&& expected == I::MIN
&& actual == I::ZERO
{
return XFAIL;
}

None
}

#[cfg(f128_enabled)]
impl MaybeOverride<(f128, f128)> for SpecialCase {
fn check_float<F: Float>(
input: (f128, f128),
_actual: F,
actual: F,
expected: F,
_ulp: &mut u32,
ctx: &CheckCtx,
) -> Option<TestResult> {
maybe_skip_binop_nan(input, expected, ctx)
binop_common(input, actual, expected, ctx)
}
}

/// Musl propagates NaNs if one is provided as the input, but we return the other input.
// F1 and F2 are always the same type, this is just to please generics
fn maybe_skip_binop_nan<F1: Float, F2: Float>(
fn binop_common<F1: Float, F2: Float>(
input: (F1, F1),
actual: F2,
expected: F2,
ctx: &CheckCtx,
) -> Option<TestResult> {
/* FIXME(#439): we do not compare signed zeros */

if ctx.base_name == BaseName::Fmin
&& input.0.biteq(F1::NEG_ZERO)
&& input.1.biteq(F1::ZERO)
&& expected.biteq(F2::NEG_ZERO)
&& actual.biteq(F2::ZERO)
{
return XFAIL;
}

if ctx.base_name == BaseName::Fmax
&& input.0.biteq(F1::NEG_ZERO)
&& input.1.biteq(F1::ZERO)
&& expected.biteq(F2::ZERO)
&& actual.biteq(F2::NEG_ZERO)
{
return XFAIL;
}

// Musl propagates NaNs if one is provided as the input, but we return the other input.
match (&ctx.basis, ctx.base_name) {
(Musl, BaseName::Fmin | BaseName::Fmax)
if (input.0.is_nan() || input.1.is_nan()) && expected.is_nan() =>
Expand Down Expand Up @@ -502,7 +572,53 @@ fn bessel_prec_dropoff<F: Float>(
None
}

impl MaybeOverride<(f32, f32, f32)> for SpecialCase {}
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {}
impl MaybeOverride<(f32, i32)> for SpecialCase {}
impl MaybeOverride<(f64, i32)> for SpecialCase {}

impl MaybeOverride<(f32, f32, f32)> for SpecialCase {
fn check_float<F: Float>(
input: (f32, f32, f32),
actual: F,
expected: F,
_ulp: &mut u32,
ctx: &CheckCtx,
) -> Option<TestResult> {
ternop_common(input, actual, expected, ctx)
}
}
impl MaybeOverride<(f64, f64, f64)> for SpecialCase {
fn check_float<F: Float>(
input: (f64, f64, f64),
actual: F,
expected: F,
_ulp: &mut u32,
ctx: &CheckCtx,
) -> Option<TestResult> {
ternop_common(input, actual, expected, ctx)
}
}

// F1 and F2 are always the same type, this is just to please generics
fn ternop_common<F1: Float, F2: Float>(
input: (F1, F1, F1),
actual: F2,
expected: F2,
ctx: &CheckCtx,
) -> Option<TestResult> {
// FIXME(fma): 754-2020 says "When the exact result of (a × b) + c is non-zero yet the result
// of fusedMultiplyAdd is zero because of rounding, the zero result takes the sign of the
// exact result". Our implementation returns the wrong sign:
// fma(5e-324, -5e-324, 0.0) = 0.0 (should be -0.0)
if ctx.base_name == BaseName::Fma
&& (input.0.is_sign_negative() ^ input.1.is_sign_negative())
&& input.0 != F1::ZERO
&& input.1 != F1::ZERO
&& input.2.biteq(F1::ZERO)
&& expected.biteq(F2::NEG_ZERO)
&& actual.biteq(F2::ZERO)
{
return XFAIL;
}

None
}
7 changes: 6 additions & 1 deletion src/math/support/float_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,14 @@ pub trait Float:
/// Returns true if the value is +inf or -inf.
fn is_infinite(self) -> bool;

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

/// Returns true if the sign is positive. Extracts the sign bit regardless of zero or NaN.
fn is_sign_positive(self) -> bool {
!self.is_sign_negative()
}

/// Returns if `self` is subnormal
fn is_subnormal(self) -> bool {
(self.to_bits() & Self::EXP_MASK) == Self::Int::ZERO
Expand Down

0 comments on commit 7788a3d

Please sign in to comment.