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

Add copysignf16, copysignf128, fabsf16, and fabsf128 #386

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions crates/libm-macros/src/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ use std::fmt;
use std::sync::LazyLock;

const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])] = &[
(
// `fn(f16) -> f16`
FloatTy::F16,
Signature { args: &[Ty::F16], returns: &[Ty::F16] },
None,
&["fabsf16"],
),
(
// `fn(f32) -> f32`
FloatTy::F32,
Expand All @@ -28,6 +35,20 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"tgamma", "trunc", "y0", "y1",
],
),
(
// `fn(f128) -> f128`
FloatTy::F128,
Signature { args: &[Ty::F128], returns: &[Ty::F128] },
None,
&["fabsf128"],
),
(
// `(f16, f16) -> f16`
FloatTy::F16,
Signature { args: &[Ty::F16, Ty::F16], returns: &[Ty::F16] },
None,
&["copysignf16"],
),
(
// `(f32, f32) -> f32`
FloatTy::F32,
Expand Down Expand Up @@ -64,6 +85,13 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option<Signature>, &[&str])]
"remainder",
],
),
(
// `(f128, f128) -> f128`
FloatTy::F128,
Signature { args: &[Ty::F128, Ty::F128], returns: &[Ty::F128] },
None,
&["copysignf128"],
),
(
// `(f32, f32, f32) -> f32`
FloatTy::F32,
Expand Down
2 changes: 1 addition & 1 deletion crates/libm-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ publish = false
default = ["unstable-float"]

# Propagated from libm because this affects which functions we test.
unstable-float = ["libm/unstable-float"]
unstable-float = ["libm/unstable-float", "rug?/nightly-float"]

# Generate tests which are random inputs and the outputs are calculated with
# musl libc.
Expand Down
47 changes: 29 additions & 18 deletions crates/libm-test/benches/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ macro_rules! musl_rand_benches {
(
fn_name: $fn_name:ident,
attrs: [$($attr:meta),*],
fn_extra: $skip_on_i586:expr,
fn_extra: ($skip_on_i586:expr, $musl_fn:expr),
) => {
paste::paste! {
$(#[$attr])*
fn [< musl_bench_ $fn_name >](c: &mut Criterion) {
type Op = libm_test::op::$fn_name::Routine;

#[cfg(feature = "build-musl")]
let musl_extra = MuslExtra {
musl_fn: Some(musl_math_sys::$fn_name as libm_test::OpCFn<Op>),
skip_on_i586: $skip_on_i586
let musl_extra = MuslExtra::<libm_test::OpCFn<Op>> {
musl_fn: $musl_fn,
skip_on_i586: $skip_on_i586,
};

#[cfg(not(feature = "build-musl"))]
let musl_extra = MuslExtra {
musl_fn: None,
skip_on_i586: $skip_on_i586
skip_on_i586: $skip_on_i586,
};

bench_one::<Op>(c, musl_extra);
Expand Down Expand Up @@ -67,7 +67,10 @@ where
break;
}

let musl_res = input.call(musl_extra.musl_fn.unwrap());
let Some(musl_fn) = musl_extra.musl_fn else {
continue;
};
let musl_res = input.call(musl_fn);
let crate_res = input.call(Op::ROUTINE);

crate_res.validate(musl_res, input, &ctx).context(name).unwrap();
Expand All @@ -91,25 +94,33 @@ where
// Don't test against musl if it is not available
#[cfg(feature = "build-musl")]
{
let musl_fn = musl_extra.musl_fn.unwrap();
group.bench_function("musl", |b| {
b.iter(|| {
let f = black_box(musl_fn);
for input in benchvec.iter().copied() {
input.call(f);
}
})
});
if let Some(musl_fn) = musl_extra.musl_fn {
group.bench_function("musl", |b| {
b.iter(|| {
let f = black_box(musl_fn);
for input in benchvec.iter().copied() {
input.call(f);
}
})
});
}
}
}

libm_macros::for_each_function! {
callback: musl_rand_benches,
skip: [],
fn_extra: match MACRO_FN_NAME {
// FIXME(correctness): wrong result on i586
exp10 | exp10f | exp2 | exp2f => true,
_ => false
// We pass a tuple of `(skip_on_i586, musl_fn)`

// FIXME(correctness): exp functions have the wrong result on i586
exp10 | exp10f | exp2 | exp2f => (true, Some(musl_math_sys::MACRO_FN_NAME)),

// Musl does not provide `f16` and `f128` functions
copysignf16 | copysignf128 | fabsf16 | fabsf128 => (false, None),

// By default we never skip (false) and always have a musl function available
_ => (false, Some(musl_math_sys::MACRO_FN_NAME))
}
}

Expand Down
12 changes: 12 additions & 0 deletions crates/libm-test/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,15 @@ impl HasDomain<f32> for crate::op::lgammaf_r::Routine {
impl HasDomain<f64> for crate::op::lgamma_r::Routine {
const DOMAIN: Domain<f64> = Domain::<f64>::LGAMMA;
}

/* Not all `f16` and `f128` functions exist yet so we can't easily use the macros. */

#[cfg(f16_enabled)]
impl HasDomain<f16> for crate::op::fabsf16::Routine {
const DOMAIN: Domain<f16> = Domain::<f16>::UNBOUNDED;
}

#[cfg(f128_enabled)]
impl HasDomain<f128> for crate::op::fabsf128::Routine {
const DOMAIN: Domain<f128> = Domain::<f128>::UNBOUNDED;
}
4 changes: 4 additions & 0 deletions crates/libm-test/src/gen/extensive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,12 @@ macro_rules! impl_extensive_input {
};
}

#[cfg(f16_enabled)]
impl_extensive_input!(f16);
impl_extensive_input!(f32);
impl_extensive_input!(f64);
#[cfg(f128_enabled)]
impl_extensive_input!(f128);

/// Create a test case iterator for extensive inputs.
pub fn get_test_cases<Op>(
Expand Down
4 changes: 4 additions & 0 deletions crates/libm-test/src/gen/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ macro_rules! impl_random_input {
};
}

#[cfg(f16_enabled)]
impl_random_input!(f16);
impl_random_input!(f32);
impl_random_input!(f64);
#[cfg(f128_enabled)]
impl_random_input!(f128);

/// Create a test case iterator.
pub fn get_test_cases<RustArgs: RandomInput>(
Expand Down
67 changes: 58 additions & 9 deletions crates/libm-test/src/mpfloat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ libm_macros::for_each_function! {
fmod, fmodf, frexp, frexpf, ilogb, ilogbf, jn, jnf, ldexp, ldexpf,
lgamma_r, lgammaf_r, modf, modff, nextafter, nextafterf, pow,powf,
remquo, remquof, scalbn, scalbnf, sincos, sincosf, yn, ynf,
copysignf16, copysignf128, fabsf16, fabsf128,
],
fn_extra: match MACRO_FN_NAME {
// Remap function names that are different between mpfr and libm
Expand All @@ -157,10 +158,8 @@ libm_macros::for_each_function! {
/// Implement unary functions that don't have a `_round` version
macro_rules! impl_no_round {
// Unary matcher
($($fn_name:ident, $rug_name:ident;)*) => {
($($fn_name:ident => $rug_name:ident;)*) => {
paste::paste! {
// Implement for both f32 and f64
$( impl_no_round!{ @inner_unary [< $fn_name f >], $rug_name } )*
$( impl_no_round!{ @inner_unary $fn_name, $rug_name } )*
}
};
Expand All @@ -183,12 +182,28 @@ macro_rules! impl_no_round {
}

impl_no_round! {
fabs, abs_mut;
ceil, ceil_mut;
floor, floor_mut;
rint, round_even_mut; // FIXME: respect rounding mode
round, round_mut;
trunc, trunc_mut;
ceil => ceil_mut;
ceilf => ceil_mut;
fabs => abs_mut;
fabsf => abs_mut;
floor => floor_mut;
floorf => floor_mut;
rint => round_even_mut; // FIXME: respect rounding mode
rintf => round_even_mut; // FIXME: respect rounding mode
round => round_mut;
roundf => round_mut;
trunc => trunc_mut;
truncf => trunc_mut;
}

#[cfg(f16_enabled)]
impl_no_round! {
fabsf16 => abs_mut;
}

#[cfg(f128_enabled)]
impl_no_round! {
fabsf128 => abs_mut;
}

/// Some functions are difficult to do in a generic way. Implement them here.
Expand Down Expand Up @@ -324,3 +339,37 @@ impl MpOp for crate::op::lgammaf_r::Routine {
(ret, sign as i32)
}
}

/* Not all `f16` and `f128` functions exist yet so we can't easily use the macros. */

#[cfg(f16_enabled)]
impl MpOp for crate::op::copysignf16::Routine {
type MpTy = (MpFloat, MpFloat);

fn new_mp() -> Self::MpTy {
(new_mpfloat::<f16>(), new_mpfloat::<f16>())
}

fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
this.0.copysign_mut(&this.1);
prep_retval::<Self::RustRet>(&mut this.0, Ordering::Equal)
}
}

#[cfg(f128_enabled)]
impl MpOp for crate::op::copysignf128::Routine {
type MpTy = (MpFloat, MpFloat);

fn new_mp() -> Self::MpTy {
(new_mpfloat::<f128>(), new_mpfloat::<f128>())
}

fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet {
this.0.assign(input.0);
this.1.assign(input.1);
this.0.copysign_mut(&this.1);
prep_retval::<Self::RustRet>(&mut this.0, Ordering::Equal)
}
}
32 changes: 32 additions & 0 deletions crates/libm-test/src/precision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ pub trait MaybeOverride<Input> {
}
}

#[cfg(f16_enabled)]
impl MaybeOverride<(f16,)> for SpecialCase {}

impl MaybeOverride<(f32,)> for SpecialCase {
fn check_float<F: Float>(
input: (f32,),
Expand Down Expand Up @@ -232,6 +235,9 @@ impl MaybeOverride<(f64,)> for SpecialCase {
}
}

#[cfg(f128_enabled)]
impl MaybeOverride<(f128,)> for SpecialCase {}

/// Check NaN bits if the function requires it
fn maybe_check_nan_bits<F: Float>(actual: F, expected: F, ctx: &CheckCtx) -> Option<TestResult> {
if !(ctx.base_name == BaseName::Fabs || ctx.base_name == BaseName::Copysign) {
Expand Down Expand Up @@ -259,6 +265,19 @@ fn maybe_check_nan_bits<F: Float>(actual: F, expected: F, ctx: &CheckCtx) -> Opt
}
}

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

impl MaybeOverride<(f32, f32)> for SpecialCase {
fn check_float<F: Float>(
input: (f32, f32),
Expand All @@ -283,6 +302,19 @@ impl MaybeOverride<(f64, f64)> for SpecialCase {
}
}

#[cfg(f128_enabled)]
impl MaybeOverride<(f128, f128)> for SpecialCase {
fn check_float<F: Float>(
input: (f128, f128),
_actual: F,
expected: F,
_ulp: &mut u32,
ctx: &CheckCtx,
) -> Option<TestResult> {
maybe_skip_binop_nan(input, 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>(
Expand Down
6 changes: 6 additions & 0 deletions crates/libm-test/src/test_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,12 @@ where

impl_float!(f32, f64);

#[cfg(f16_enabled)]
impl_float!(f16);

#[cfg(f128_enabled)]
impl_float!(f128);

/* trait implementations for compound types */

/// Implement `CheckOutput` for combinations of types.
Expand Down
2 changes: 2 additions & 0 deletions crates/libm-test/tests/compare_built_musl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ where

libm_macros::for_each_function! {
callback: musl_rand_tests,
// Musl does not support `f16` and `f128` on all platforms.
skip: [copysignf16, copysignf128, fabsf16, fabsf128],
attributes: [
#[cfg_attr(x86_no_sse, ignore)] // FIXME(correctness): wrong result on i586
[exp10, exp10f, exp2, exp2f, rint]
Expand Down
2 changes: 2 additions & 0 deletions crates/libm-test/tests/multiprecision.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ libm_macros::for_each_function! {
atan2f,
copysign,
copysignf,
copysignf16,
copysignf128,
fdim,
fdimf,
fma,
Expand Down
Loading