From b7b8a26e2810d700bae947312e1338bb2a5fe2b2 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sun, 20 Apr 2025 04:21:18 +0000 Subject: [PATCH] Add `frexpf16`, `frexpf128`, `ilogbf16`, and `ilogbf128` --- crates/libm-macros/src/shared.rs | 46 ++++++++++++++++ crates/util/src/main.rs | 4 ++ libm-test/benches/random.rs | 4 ++ libm-test/src/generate/case_list.rs | 24 ++++++++- libm-test/src/mpfloat.rs | 78 ++++++++++++++------------- libm-test/src/test_traits.rs | 12 +++++ libm-test/tests/compare_built_musl.rs | 4 ++ libm/src/math/frexp.rs | 46 ++++++++++------ libm/src/math/frexpf.rs | 22 -------- libm/src/math/generic/frexp.rs | 25 +++++++++ libm/src/math/generic/ilogb.rs | 34 ++++++++++++ libm/src/math/generic/mod.rs | 4 ++ libm/src/math/ilogb.rs | 47 +++++++--------- libm/src/math/ilogbf.rs | 28 ---------- libm/src/math/mod.rs | 12 ++--- 15 files changed, 251 insertions(+), 139 deletions(-) delete mode 100644 libm/src/math/frexpf.rs create mode 100644 libm/src/math/generic/frexp.rs create mode 100644 libm/src/math/generic/ilogb.rs delete mode 100644 libm/src/math/ilogbf.rs diff --git a/crates/libm-macros/src/shared.rs b/crates/libm-macros/src/shared.rs index 750ed1afb..31ef9c8e2 100644 --- a/crates/libm-macros/src/shared.rs +++ b/crates/libm-macros/src/shared.rs @@ -258,6 +258,16 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] None, &["fmaf128"], ), + ( + // `(f16) -> i32` + FloatTy::F16, + Signature { + args: &[Ty::F16], + returns: &[Ty::I32], + }, + None, + &["ilogbf16"], + ), ( // `(f32) -> i32` FloatTy::F32, @@ -278,6 +288,16 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] None, &["ilogb"], ), + ( + // `(f128) -> i32` + FloatTy::F128, + Signature { + args: &[Ty::F128], + returns: &[Ty::I32], + }, + None, + &["ilogbf128"], + ), ( // `(i32, f32) -> f32` FloatTy::F32, @@ -364,6 +384,19 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] }), &["modf"], ), + ( + // `(f16, &mut c_int) -> f16` as `(f16) -> (f16, i32)` + FloatTy::F16, + Signature { + args: &[Ty::F16], + returns: &[Ty::F16, Ty::I32], + }, + Some(Signature { + args: &[Ty::F16, Ty::MutCInt], + returns: &[Ty::F16], + }), + &["frexpf16"], + ), ( // `(f32, &mut c_int) -> f32` as `(f32) -> (f32, i32)` FloatTy::F32, @@ -390,6 +423,19 @@ const ALL_OPERATIONS_NESTED: &[(FloatTy, Signature, Option, &[&str])] }), &["frexp", "lgamma_r"], ), + ( + // `(f128, &mut c_int) -> f128` as `(f128) -> (f128, i32)` + FloatTy::F128, + Signature { + args: &[Ty::F128], + returns: &[Ty::F128, Ty::I32], + }, + Some(Signature { + args: &[Ty::F128, Ty::MutCInt], + returns: &[Ty::F128], + }), + &["frexpf128"], + ), ( // `(f32, f32, &mut c_int) -> f32` as `(f32, f32) -> (f32, i32)` FloatTy::F32, diff --git a/crates/util/src/main.rs b/crates/util/src/main.rs index e70578699..a7b4a4e6c 100644 --- a/crates/util/src/main.rs +++ b/crates/util/src/main.rs @@ -119,6 +119,10 @@ fn do_eval(basis: &str, op: &str, inputs: &[&str]) { | fminimumf16 | fmodf128 | fmodf16 + | frexpf128 + | frexpf16 + | ilogbf128 + | ilogbf16 | ldexpf128 | ldexpf16 | rintf128 diff --git a/libm-test/benches/random.rs b/libm-test/benches/random.rs index 81f58e3a6..55a6f4e65 100644 --- a/libm-test/benches/random.rs +++ b/libm-test/benches/random.rs @@ -159,6 +159,10 @@ libm_macros::for_each_function! { | fminimumf16 | fmodf128 | fmodf16 + | frexpf128 + | frexpf16 + | ilogbf128 + | ilogbf16 | ldexpf128 | ldexpf16 | rintf128 diff --git a/libm-test/src/generate/case_list.rs b/libm-test/src/generate/case_list.rs index f1e6fcec3..aa90ede41 100644 --- a/libm-test/src/generate/case_list.rs +++ b/libm-test/src/generate/case_list.rs @@ -449,7 +449,8 @@ fn fmodf16_cases() -> Vec> { vec![] } -fn frexp_cases() -> Vec> { +#[cfg(f16_enabled)] +fn frexpf16_cases() -> Vec> { vec![] } @@ -457,6 +458,15 @@ fn frexpf_cases() -> Vec> { vec![] } +fn frexp_cases() -> Vec> { + vec![] +} + +#[cfg(f128_enabled)] +fn frexpf128_cases() -> Vec> { + vec![] +} + fn hypot_cases() -> Vec> { vec![] } @@ -465,7 +475,8 @@ fn hypotf_cases() -> Vec> { vec![] } -fn ilogb_cases() -> Vec> { +#[cfg(f16_enabled)] +fn ilogbf16_cases() -> Vec> { vec![] } @@ -473,6 +484,15 @@ fn ilogbf_cases() -> Vec> { vec![] } +fn ilogb_cases() -> Vec> { + vec![] +} + +#[cfg(f128_enabled)] +fn ilogbf128_cases() -> Vec> { + vec![] +} + fn j0_cases() -> Vec> { vec![] } diff --git a/libm-test/src/mpfloat.rs b/libm-test/src/mpfloat.rs index 9b51dc605..e121ef2bc 100644 --- a/libm-test/src/mpfloat.rs +++ b/libm-test/src/mpfloat.rs @@ -162,8 +162,12 @@ libm_macros::for_each_function! { fmodf16, frexp, frexpf, + frexpf128, + frexpf16, ilogb, ilogbf, + ilogbf128, + ilogbf16, jn, jnf, ldexp, @@ -323,43 +327,6 @@ macro_rules! impl_op_for_ty { } } - impl MpOp for crate::op::[]::Routine { - type MpTy = MpFloat; - - fn new_mp() -> Self::MpTy { - new_mpfloat::() - } - - fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { - this.assign(input.0); - let exp = this.frexp_mut(); - (prep_retval::(this, Ordering::Equal), exp) - } - } - - impl MpOp for crate::op::[]::Routine { - type MpTy = MpFloat; - - fn new_mp() -> Self::MpTy { - new_mpfloat::() - } - - fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { - this.assign(input.0); - - // `get_exp` follows `frexp` for `0.5 <= |m| < 1.0`. Adjust the exponent by - // one to scale the significand to `1.0 <= |m| < 2.0`. - this.get_exp().map(|v| v - 1).unwrap_or_else(|| { - if this.is_infinite() { - i32::MAX - } else { - // Zero or NaN - i32::MIN - } - }) - } - } - impl MpOp for crate::op::[]::Routine { type MpTy = MpFloat; @@ -504,6 +471,43 @@ macro_rules! impl_op_for_ty_all { } } + impl MpOp for crate::op::[]::Routine { + type MpTy = MpFloat; + + fn new_mp() -> Self::MpTy { + new_mpfloat::() + } + + fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { + this.assign(input.0); + let exp = this.frexp_mut(); + (prep_retval::(this, Ordering::Equal), exp) + } + } + + impl MpOp for crate::op::[]::Routine { + type MpTy = MpFloat; + + fn new_mp() -> Self::MpTy { + new_mpfloat::() + } + + fn run(this: &mut Self::MpTy, input: Self::RustArgs) -> Self::RustRet { + this.assign(input.0); + + // `get_exp` follows `frexp` for `0.5 <= |m| < 1.0`. Adjust the exponent by + // one to scale the significand to `1.0 <= |m| < 2.0`. + this.get_exp().map(|v| v - 1).unwrap_or_else(|| { + if this.is_infinite() { + i32::MAX + } else { + // Zero or NaN + i32::MIN + } + }) + } + } + // `ldexp` and `scalbn` are the same for binary floating point, so just forward all // methods. impl MpOp for crate::op::[]::Routine { diff --git a/libm-test/src/test_traits.rs b/libm-test/src/test_traits.rs index dbb970161..644d25387 100644 --- a/libm-test/src/test_traits.rs +++ b/libm-test/src/test_traits.rs @@ -451,3 +451,15 @@ impl_tuples!( (f32, f32); (f64, f64); ); + +#[cfg(f16_enabled)] +impl_tuples!( + (f16, i32); + (f16, f16); +); + +#[cfg(f128_enabled)] +impl_tuples!( + (f128, i32); + (f128, f128); +); diff --git a/libm-test/tests/compare_built_musl.rs b/libm-test/tests/compare_built_musl.rs index cbb4bd49b..24a4d0a64 100644 --- a/libm-test/tests/compare_built_musl.rs +++ b/libm-test/tests/compare_built_musl.rs @@ -122,6 +122,10 @@ libm_macros::for_each_function! { fminimumf16, fmodf128, fmodf16, + frexpf128, + frexpf16, + ilogbf128, + ilogbf16, ldexpf128, ldexpf16, rintf128, diff --git a/libm/src/math/frexp.rs b/libm/src/math/frexp.rs index de7a64fda..102be11db 100644 --- a/libm/src/math/frexp.rs +++ b/libm/src/math/frexp.rs @@ -1,21 +1,33 @@ +/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2. +/// +/// That is, `x * 2^p` will represent the input value. +#[cfg(f16_enabled)] #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn frexp(x: f64) -> (f64, i32) { - let mut y = x.to_bits(); - let ee = ((y >> 52) & 0x7ff) as i32; +pub fn frexpf16(x: f16) -> (f16, i32) { + super::generic::frexp(x) +} - if ee == 0 { - if x != 0.0 { - let x1p64 = f64::from_bits(0x43f0000000000000); - let (x, e) = frexp(x * x1p64); - return (x, e - 64); - } - return (x, 0); - } else if ee == 0x7ff { - return (x, 0); - } +/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2. +/// +/// That is, `x * 2^p` will represent the input value. +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn frexpf(x: f32) -> (f32, i32) { + super::generic::frexp(x) +} - let e = ee - 0x3fe; - y &= 0x800fffffffffffff; - y |= 0x3fe0000000000000; - return (f64::from_bits(y), e); +/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2. +/// +/// That is, `x * 2^p` will represent the input value. +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn frexp(x: f64) -> (f64, i32) { + super::generic::frexp(x) +} + +/// Decompose a float into a normalized value within the range `[0.5, 1)`, and a power of 2. +/// +/// That is, `x * 2^p` will represent the input value. +#[cfg(f128_enabled)] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn frexpf128(x: f128) -> (f128, i32) { + super::generic::frexp(x) } diff --git a/libm/src/math/frexpf.rs b/libm/src/math/frexpf.rs deleted file mode 100644 index 0ec91c2d3..000000000 --- a/libm/src/math/frexpf.rs +++ /dev/null @@ -1,22 +0,0 @@ -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn frexpf(x: f32) -> (f32, i32) { - let mut y = x.to_bits(); - let ee: i32 = ((y >> 23) & 0xff) as i32; - - if ee == 0 { - if x != 0.0 { - let x1p64 = f32::from_bits(0x5f800000); - let (x, e) = frexpf(x * x1p64); - return (x, e - 64); - } else { - return (x, 0); - } - } else if ee == 0xff { - return (x, 0); - } - - let e = ee - 0x7e; - y &= 0x807fffff; - y |= 0x3f000000; - (f32::from_bits(y), e) -} diff --git a/libm/src/math/generic/frexp.rs b/libm/src/math/generic/frexp.rs new file mode 100644 index 000000000..e560e0374 --- /dev/null +++ b/libm/src/math/generic/frexp.rs @@ -0,0 +1,25 @@ +use super::super::{CastFrom, Float, MinInt}; + +pub fn frexp(x: F) -> (F, i32) { + let mut ix = x.to_bits(); + let ee = x.ex(); + + if ee == 0 { + if x != F::ZERO { + // normalize via multiplication; 1p64 for `f64` + let magic = F::from_parts(false, F::EXP_BIAS + F::BITS, F::Int::ZERO); + magic.to_bits(); + + let (x, e) = frexp(x * magic); + return (x, e - F::BITS as i32); + } + return (x, 0); + } else if ee == F::EXP_SAT { + return (x, 0); + } + + let e = ee as i32 - (F::EXP_BIAS as i32 - 1); + ix &= F::SIGN_MASK | F::SIG_MASK; + ix |= F::Int::cast_from(F::EXP_BIAS - 1) << F::SIG_BITS; + (F::from_bits(ix), e) +} diff --git a/libm/src/math/generic/ilogb.rs b/libm/src/math/generic/ilogb.rs new file mode 100644 index 000000000..dcc474592 --- /dev/null +++ b/libm/src/math/generic/ilogb.rs @@ -0,0 +1,34 @@ +use super::super::{Float, MinInt}; + +const FP_ILOGBNAN: i32 = i32::MIN; +const FP_ILOGB0: i32 = FP_ILOGBNAN; + +pub fn ilogb(x: F) -> i32 { + let zero = F::Int::ZERO; + let mut i = x.to_bits(); + let e = x.ex() as i32; + + if e == 0 { + i <<= F::EXP_BITS + 1; + if i == F::Int::ZERO { + force_eval!(0.0 / 0.0); + return FP_ILOGB0; + } + /* subnormal x */ + let mut e = -(F::EXP_BIAS as i32); + while i >> (F::BITS - 1) == zero { + e -= 1; + i <<= 1; + } + e + } else if e == F::EXP_SAT as i32 { + force_eval!(0.0 / 0.0); + if i << (F::EXP_BITS + 1) != zero { + FP_ILOGBNAN + } else { + i32::MAX + } + } else { + e - F::EXP_BIAS as i32 + } +} diff --git a/libm/src/math/generic/mod.rs b/libm/src/math/generic/mod.rs index 35846351a..9812452bc 100644 --- a/libm/src/math/generic/mod.rs +++ b/libm/src/math/generic/mod.rs @@ -13,6 +13,8 @@ mod fmin; mod fminimum; mod fminimum_num; mod fmod; +mod frexp; +mod ilogb; mod rint; mod round; mod scalbn; @@ -31,6 +33,8 @@ pub use fmin::fmin; pub use fminimum::fminimum; pub use fminimum_num::fminimum_num; pub use fmod::fmod; +pub use frexp::frexp; +pub use ilogb::ilogb; pub use rint::rint_round; pub use round::round; pub use scalbn::scalbn; diff --git a/libm/src/math/ilogb.rs b/libm/src/math/ilogb.rs index 5b41f7b1d..c1427476f 100644 --- a/libm/src/math/ilogb.rs +++ b/libm/src/math/ilogb.rs @@ -1,32 +1,25 @@ -const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; -const FP_ILOGB0: i32 = FP_ILOGBNAN; +/// Extract the binary exponent of `x`. +#[cfg(f16_enabled)] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn ilogbf16(x: f16) -> i32 { + super::generic::ilogb(x) +} +/// Extract the binary exponent of `x`. +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn ilogbf(x: f32) -> i32 { + super::generic::ilogb(x) +} + +/// Extract the binary exponent of `x`. #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] pub fn ilogb(x: f64) -> i32 { - let mut i: u64 = x.to_bits(); - let e = ((i >> 52) & 0x7ff) as i32; + super::generic::ilogb(x) +} - if e == 0 { - i <<= 12; - if i == 0 { - force_eval!(0.0 / 0.0); - return FP_ILOGB0; - } - /* subnormal x */ - let mut e = -0x3ff; - while (i >> 63) == 0 { - e -= 1; - i <<= 1; - } - e - } else if e == 0x7ff { - force_eval!(0.0 / 0.0); - if (i << 12) != 0 { - FP_ILOGBNAN - } else { - i32::MAX - } - } else { - e - 0x3ff - } +/// Extract the binary exponent of `x`. +#[cfg(f128_enabled)] +#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +pub fn ilogbf128(x: f128) -> i32 { + super::generic::ilogb(x) } diff --git a/libm/src/math/ilogbf.rs b/libm/src/math/ilogbf.rs deleted file mode 100644 index 3585d6d36..000000000 --- a/libm/src/math/ilogbf.rs +++ /dev/null @@ -1,28 +0,0 @@ -const FP_ILOGBNAN: i32 = -1 - 0x7fffffff; -const FP_ILOGB0: i32 = FP_ILOGBNAN; - -#[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] -pub fn ilogbf(x: f32) -> i32 { - let mut i = x.to_bits(); - let e = ((i >> 23) & 0xff) as i32; - - if e == 0 { - i <<= 9; - if i == 0 { - force_eval!(0.0 / 0.0); - return FP_ILOGB0; - } - /* subnormal x */ - let mut e = -0x7f; - while (i >> 31) == 0 { - e -= 1; - i <<= 1; - } - e - } else if e == 0xff { - force_eval!(0.0 / 0.0); - if (i << 9) != 0 { FP_ILOGBNAN } else { i32::MAX } - } else { - e - 0x7f - } -} diff --git a/libm/src/math/mod.rs b/libm/src/math/mod.rs index 949c18b40..30fc0e0c4 100644 --- a/libm/src/math/mod.rs +++ b/libm/src/math/mod.rs @@ -165,11 +165,9 @@ mod fminimum_fmaximum; mod fminimum_fmaximum_num; mod fmod; mod frexp; -mod frexpf; mod hypot; mod hypotf; mod ilogb; -mod ilogbf; mod j0; mod j0f; mod j1; @@ -260,12 +258,10 @@ pub use self::fmin_fmax::{fmax, fmaxf, fmin, fminf}; pub use self::fminimum_fmaximum::{fmaximum, fmaximumf, fminimum, fminimumf}; pub use self::fminimum_fmaximum_num::{fmaximum_num, fmaximum_numf, fminimum_num, fminimum_numf}; pub use self::fmod::{fmod, fmodf}; -pub use self::frexp::frexp; -pub use self::frexpf::frexpf; +pub use self::frexp::{frexp, frexpf}; pub use self::hypot::hypot; pub use self::hypotf::hypotf; -pub use self::ilogb::ilogb; -pub use self::ilogbf::ilogbf; +pub use self::ilogb::{ilogb, ilogbf}; pub use self::j0::{j0, y0}; pub use self::j0f::{j0f, y0f}; pub use self::j1::{j1, y1}; @@ -326,6 +322,8 @@ cfg_if! { pub use self::fminimum_fmaximum::{fmaximumf16, fminimumf16}; pub use self::fminimum_fmaximum_num::{fmaximum_numf16, fminimum_numf16}; pub use self::fmod::fmodf16; + pub use self::frexp::frexpf16; + pub use self::ilogb::ilogbf16; pub use self::ldexp::ldexpf16; pub use self::rint::rintf16; pub use self::round::roundf16; @@ -353,6 +351,8 @@ cfg_if! { pub use self::fminimum_fmaximum::{fmaximumf128, fminimumf128}; pub use self::fminimum_fmaximum_num::{fmaximum_numf128, fminimum_numf128}; pub use self::fmod::fmodf128; + pub use self::frexp::frexpf128; + pub use self::ilogb::ilogbf128; pub use self::ldexp::ldexpf128; pub use self::rint::rintf128; pub use self::round::roundf128;