From af263a6ae232d016544b9209af2b40f22705edce Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 11 Feb 2025 08:55:57 +0000 Subject: [PATCH] Initial coarse pass --- crates/libm-test/src/domain.rs | 2 +- crates/libm-test/src/f8_impl.rs | 7 + etc/consts-cbrt.jl | 23 +++ etc/update-api-list.py | 4 + src/math/cbrt.rs | 316 +++++++++++++++++++++---------- src/math/mod.rs | 1 + src/math/support/float_traits.rs | 14 ++ 7 files changed, 261 insertions(+), 106 deletions(-) create mode 100644 etc/consts-cbrt.jl diff --git a/crates/libm-test/src/domain.rs b/crates/libm-test/src/domain.rs index 41e948461..4a0b47028 100644 --- a/crates/libm-test/src/domain.rs +++ b/crates/libm-test/src/domain.rs @@ -171,7 +171,7 @@ impl EitherPrim, Domain> { Box::new((0..u8::MAX).map(|scale| { let mut base = F::ZERO; for _ in 0..scale { - base = base - F::ONE; + base -= F::ONE; } base })) diff --git a/crates/libm-test/src/f8_impl.rs b/crates/libm-test/src/f8_impl.rs index 56ea0b729..331588a8a 100644 --- a/crates/libm-test/src/f8_impl.rs +++ b/crates/libm-test/src/f8_impl.rs @@ -39,6 +39,13 @@ impl Float for f8 { const NEG_PI: Self = Self::ZERO; const FRAC_PI_2: Self = Self::ZERO; + /// `2^sig_bits` + const TWO_POW_SIG_BITS: Self = + Self(((Self::SIG_BITS + Self::EXP_BIAS) as Self::Int) << Self::SIG_BITS); + /// `2^-sig_bits` + const TWO_POW_NEG_SIG_BITS: Self = + Self(((-(Self::SIG_BITS as i32) + Self::EXP_BIAS as i32) as Self::Int) << Self::SIG_BITS); + const BITS: u32 = 8; const SIG_BITS: u32 = 3; const SIGN_MASK: Self::Int = 0b1_0000_000; diff --git a/etc/consts-cbrt.jl b/etc/consts-cbrt.jl new file mode 100644 index 000000000..6b85143a5 --- /dev/null +++ b/etc/consts-cbrt.jl @@ -0,0 +1,23 @@ + +using Printf +using Remez + +function main() + run_one("f64", "hf64!", 53) +end + +function run_one(name::String, hf::String, precision::Integer) + setprecision(precision) + + println("Constants for ", name) + + println("const ESCALE: [Self; 3] = [") + for n in 0:2 + val = big(2) ^ (n / 3) + @printf " %s(\"%a\"),\n" hf val + end + print("];") + +end + +main() diff --git a/etc/update-api-list.py b/etc/update-api-list.py index c0b6e41d3..61f41b1b1 100755 --- a/etc/update-api-list.py +++ b/etc/update-api-list.py @@ -25,6 +25,8 @@ # These files do not trigger a retest. IGNORED_SOURCES = ["src/libm_helper.rs"] +# Same as above, limited to specific functions +IGNORED_SOURCES_MAP = {"fma": ["src/math/cbrt.rs"]} IndexTy: TypeAlias = dict[str, dict[str, Any]] """Type of the `index` item in rustdoc's JSON output""" @@ -138,6 +140,8 @@ def _init_defs(self, index: IndexTy) -> None: for src in IGNORED_SOURCES: sources.discard(src) + for src in IGNORED_SOURCES_MAP.get(name, []): + sources.discard(src) # Sort the set self.defs = {k: sorted(v) for (k, v) in defs.items()} diff --git a/src/math/cbrt.rs b/src/math/cbrt.rs index 8560d37ab..6920565da 100644 --- a/src/math/cbrt.rs +++ b/src/math/cbrt.rs @@ -5,7 +5,7 @@ */ use super::Float; -use super::support::{FpResult, Round, cold_path}; +use super::support::{CastFrom, FpResult, Int, MinInt, Round, cold_path}; /// Compute the cube root of the argument. #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] @@ -13,128 +13,131 @@ pub fn cbrt(x: f64) -> f64 { cbrt_round(x, Round::Nearest).val } -pub fn cbrt_round(x: f64, round: Round) -> FpResult { - const ESCALE: [f64; 3] = [ - 1.0, - hf64!("0x1.428a2f98d728bp+0"), /* 2^(1/3) */ - hf64!("0x1.965fea53d6e3dp+0"), /* 2^(2/3) */ - ]; - - /* the polynomial c0+c1*x+c2*x^2+c3*x^3 approximates x^(1/3) on [1,2] - with maximal error < 9.2e-5 (attained at x=2) */ - const C: [f64; 4] = [ - hf64!("0x1.1b0babccfef9cp-1"), - hf64!("0x1.2c9a3e94d1da5p-1"), - hf64!("-0x1.4dc30b1a1ddbap-3"), - hf64!("0x1.7a8d3e4ec9b07p-6"), - ]; - - let u0: f64 = hf64!("0x1.5555555555555p-2"); - let u1: f64 = hf64!("0x1.c71c71c71c71cp-3"); - - let rsc = [1.0, -1.0, 0.5, -0.5, 0.25, -0.25]; - - let off = [hf64!("0x1p-53"), 0.0, 0.0, 0.0]; - - /* rm=0 for rounding to nearest, and other values for directed roundings */ - let hx: u64 = x.to_bits(); - let mut mant: u64 = hx & f64::SIG_MASK; - let sign: u64 = hx >> 63; - - let mut e: u32 = (hx >> f64::SIG_BITS) as u32 & f64::EXP_SAT; - - if ((e + 1) & f64::EXP_SAT) < 2 { +// /// Compute the cube root of the argument. +// #[cfg(f128_enabled)] +// #[cfg_attr(all(test, assert_no_panic), no_panic::no_panic)] +// pub fn cbrtf128(x: f128) -> f128 { +// cbrt_round(x, Round::Nearest).val +// } + +/// Correctly rounded cube root. +/// +/// Algorithm: +/// - Minimax initial approximation +/// - `F`-sized newton iteration +/// - `2xF`-sized newton iteration +pub fn cbrt_round(x: F, round: Round) -> FpResult +where + F::Int: CastFrom, + F::Int: CastFrom, + F::Int: From, +{ + let zero = F::Int::ZERO; + let one = F::Int::ONE; + let u0: F = F::U0; + let u1: F = F::U1; + let off = F::OFF; + + let hx = x.to_bits(); + let mut mant: F::Int = hx & F::SIG_MASK; + let sign: F::Int = hx & F::SIGN_MASK; + let neg = x.is_sign_negative(); + + let mut e: u32 = x.exp(); + + // Handle 0, infinity, NaN, and subnormals + if ((e + 1) & F::EXP_SAT) < 2 { cold_path(); - let ix: u64 = hx & !f64::SIGN_MASK; + let ix = hx & !F::SIGN_MASK; - /* 0, inf, nan: we return x + x instead of simply x, - to that for x a signaling NaN, it correctly triggers - the invalid exception. */ - if e == f64::EXP_SAT || ix == 0 { + if e == F::EXP_SAT || ix == zero { + // 0, infinity, NaN; use x + x to trigger exceptions return FpResult::ok(x + x); } - let nz = ix.leading_zeros() - 11; /* subnormal */ + // Normalize subnormals + let nz = ix.leading_zeros() - F::EXP_BITS; mant <<= nz; - mant &= f64::SIG_MASK; + mant &= F::SIG_MASK; e = e.wrapping_sub(nz - 1); } e = e.wrapping_add(3072); - let cvt1: u64 = mant | (0x3ffu64 << 52); - let mut cvt5: u64 = cvt1; + // Set the exponent to 0, z is now [1, 2) + let iz = mant | (F::Int::cast_from(F::EXP_BIAS) << F::SIG_BITS); let et: u32 = e / 3; let it: u32 = e % 3; - /* 2^(3k+it) <= x < 2^(3k+it+1), with 0 <= it <= 3 */ - cvt5 += u64::from(it) << f64::SIG_BITS; - cvt5 |= sign << 63; - let zz: f64 = f64::from_bits(cvt5); + // 2^(3k+it) <= x < 2^(3k+it+1), with 0 <= it <= 3 + // `zz` is `x` reduced to [1, 8) + let izz = (iz + (F::Int::cast_from(it) << F::SIG_BITS)) | sign; + let zz: F = F::from_bits(izz); /* cbrt(x) = cbrt(zz)*2^(et-1365) where 1 <= zz < 8 */ - let mut isc: u64 = ESCALE[it as usize].to_bits(); // todo: index - isc |= sign << 63; - let cvt2: u64 = isc; - let z: f64 = f64::from_bits(cvt1); + let isc = F::ESCALE[it as usize].to_bits() | sign; + let z: F = F::from_bits(iz); /* cbrt(zz) = cbrt(z)*isc, where isc encodes 1, 2^(1/3) or 2^(2/3), and 1 <= z < 2 */ - let r: f64 = 1.0 / z; - let rr: f64 = r * rsc[((it as usize) << 1) | sign as usize]; - let z2: f64 = z * z; - let c0: f64 = C[0] + z * C[1]; - let c2: f64 = C[2] + z * C[3]; - let mut y: f64 = c0 + z2 * c2; - let mut y2: f64 = y * y; + let r: F = F::ONE / z; + let rr: F = r * F::RSCALE[((it as usize) << 1) | neg as usize]; + let z2: F = z * z; + let c0: F = F::C[0] + z * F::C[1]; + let c2: F = F::C[2] + z * F::C[3]; /* y is an approximation of z^(1/3) */ - let mut h: f64 = y2 * (y * r) - 1.0; + let mut y: F = c0 + z2 * c2; + let mut y2: F = y * y; /* h determines the error between y and z^(1/3) */ - y -= (h * y) * (u0 - u1 * h); + let mut h: F = y2 * (y * r) - F::ONE; /* The correction y -= (h*y)*(u0 - u1*h) corresponds to a cubic variant of Newton's method, with the function f(y) = 1-z/y^3. */ - y *= f64::from_bits(cvt2); + y -= (h * y) * (u0 - u1 * h); + + y *= F::from_bits(isc); /* Now y is an approximation of zz^(1/3), * and rr an approximation of 1/zz. We now perform another iteration of * Newton-Raphson, this time with a linear approximation only. */ y2 = y * y; - let mut y2l: f64 = fmaf64(y, y, -y2); + let mut y2l: F = y.fma(y, -y2); /* y2 + y2l = y^2 exactly */ - let mut y3: f64 = y2 * y; - let mut y3l: f64 = fmaf64(y, y2, -y3) + y * y2l; + let mut y3: F = y2 * y; + let mut y3l: F = y.fma(y2, -y3) + y * y2l; /* y3 + y3l approximates y^3 with about 106 bits of accuracy */ h = ((y3 - zz) + y3l) * rr; - let mut dy: f64 = h * (y * u0); + let mut dy: F = h * (y * u0); /* the approximation of zz^(1/3) is y - dy */ - let mut y1: f64 = y - dy; + let mut y1: F = y - dy; dy = (y - y1) - dy; /* the approximation of zz^(1/3) is now y1 + dy, where |dy| < 1/2 ulp(y) * (for rounding to nearest) */ - let mut ady: f64 = dy.abs(); + let mut ady: F = dy.abs(); /* For directed roundings, ady0 is tiny when dy is tiny, or ady0 is near * from ulp(1); * for rounding to nearest, ady0 is tiny when dy is near from 1/2 ulp(1), * or from 3/2 ulp(1). */ - let mut ady0: f64 = (ady - off[round as usize]).abs(); - let mut ady1: f64 = (ady - (hf64!("0x1p-52") + off[round as usize])).abs(); + let mut ady0: F = (ady - off[round as usize]).abs(); + let mut ady1: F = (ady - (F::TWO_POW_NEG_SIG_BITS + off[round as usize])).abs(); - if ady0 < hf64!("0x1p-75") || ady1 < hf64!("0x1p-75") { + let magic = F::from_parts(false, (-75 + F::EXP_BIAS as i32) as u32, zero); + + if ady0 < magic || ady1 < magic { cold_path(); y2 = y1 * y1; - y2l = fmaf64(y1, y1, -y2); + y2l = y1.fma(y1, -y2); y3 = y2 * y1; - y3l = fmaf64(y1, y2, -y3) + y1 * y2l; + y3l = y1.fma(y2, -y3) + y1 * y2l; h = ((y3 - zz) + y3l) * rr; dy = h * (y1 * u0); y = y1 - dy; @@ -142,36 +145,31 @@ pub fn cbrt_round(x: f64, round: Round) -> FpResult { y1 = y; ady = dy.abs(); ady0 = (ady - off[round as usize]).abs(); - ady1 = (ady - (hf64!("0x1p-52") + off[round as usize])).abs(); + ady1 = (ady - (F::TWO_POW_NEG_SIG_BITS + off[round as usize])).abs(); - if ady0 < hf64!("0x1p-98") || ady1 < hf64!("0x1p-98") { + let magic2 = F::from_parts(false, (-98 + F::EXP_BIAS as i32) as u32, zero); + if ady0 < magic2 || ady1 < magic2 { cold_path(); - let azz: f64 = zz.abs(); + let azz: F = zz.abs(); // ~ 0x1.79d15d0e8d59b80000000000000ffc3dp+0 - if azz == hf64!("0x1.9b78223aa307cp+1") { - y1 = hf64!("0x1.79d15d0e8d59cp+0").copysign(zz); + if azz == F::AZMAGIC1 { + y1 = F::AZMAGIC2.copysign(zz); } // ~ 0x1.de87aa837820e80000000000001c0f08p+0 - if azz == hf64!("0x1.a202bfc89ddffp+2") { - y1 = hf64!("0x1.de87aa837820fp+0").copysign(zz); + if azz == F::AZMAGIC3 { + y1 = F::AZMAGIC4.copysign(zz); } if round != Round::Nearest { - let wlist = [ - (hf64!("0x1.3a9ccd7f022dbp+0"), hf64!("0x1.1236160ba9b93p+0")), // ~ 0x1.1236160ba9b930000000000001e7e8fap+0 - (hf64!("0x1.7845d2faac6fep+0"), hf64!("0x1.23115e657e49cp+0")), // ~ 0x1.23115e657e49c0000000000001d7a799p+0 - (hf64!("0x1.d1ef81cbbbe71p+0"), hf64!("0x1.388fb44cdcf5ap+0")), // ~ 0x1.388fb44cdcf5a0000000000002202c55p+0 - (hf64!("0x1.0a2014f62987cp+1"), hf64!("0x1.46bcbf47dc1e8p+0")), // ~ 0x1.46bcbf47dc1e8000000000000303aa2dp+0 - (hf64!("0x1.fe18a044a5501p+1"), hf64!("0x1.95decfec9c904p+0")), // ~ 0x1.95decfec9c9040000000000000159e8ep+0 - (hf64!("0x1.a6bb8c803147bp+2"), hf64!("0x1.e05335a6401dep+0")), // ~ 0x1.e05335a6401de00000000000027ca017p+0 - (hf64!("0x1.ac8538a031cbdp+2"), hf64!("0x1.e281d87098de8p+0")), // ~ 0x1.e281d87098de80000000000000ee9314p+0 - ]; - - for (a, b) in wlist { + for (a, b) in F::WLIST { if azz == a { - let tmp = if round as u64 + sign == 2 { hf64!("0x1p-52") } else { 0.0 }; + let tmp = if F::Int::from(round as u8 + neg as u8) == F::Int::cast_from(2) { + F::TWO_POW_NEG_SIG_BITS + } else { + F::ZERO + }; y1 = (b + tmp).copysign(zz); } } @@ -179,34 +177,142 @@ pub fn cbrt_round(x: f64, round: Round) -> FpResult { } } - let mut cvt3: u64 = y1.to_bits(); - cvt3 = cvt3.wrapping_add(((et.wrapping_sub(342).wrapping_sub(1023)) as u64) << 52); - let m0: u64 = cvt3 << 30; + let mut cvt3 = y1.to_bits(); + cvt3 = cvt3.wrapping_add((F::Int::cast_from(et.wrapping_sub(342).wrapping_sub(1023))) << 52); + let m0 = cvt3 << 30; let m1 = m0 >> 63; - if (m0 ^ m1) <= (1u64 << 30) { + if (m0 ^ m1) <= (one << 30) { cold_path(); - let mut cvt4: u64 = y1.to_bits(); - cvt4 = (cvt4 + (164 << 15)) & 0xffffffffffff0000u64; + let mut cvt4 = y1.to_bits(); + cvt4 = (cvt4 + (F::Int::cast_from(164) << 15)) & F::Int::cast_from(0xffffffffffff0000u64); - if ((f64::from_bits(cvt4) - y1) - dy).abs() < hf64!("0x1p-60") || (zz).abs() == 1.0 { - cvt3 = (cvt3 + (1u64 << 15)) & 0xffffffffffff0000u64; + let magic3 = F::from_parts(false, (-60 + F::EXP_BIAS as i32) as u32, zero); + if ((F::from_bits(cvt4) - y1) - dy).abs() < magic3 || (zz).abs() == F::ONE { + cvt3 = (cvt3 + (one << 15)) & F::Int::cast_from(0xffffffffffff0000u64); } } - FpResult::ok(f64::from_bits(cvt3)) + FpResult::ok(F::from_bits(cvt3)) +} + +pub trait CbrtHelper: Float { + /// 2^(n / 3) for n = [0, 1, 2] + const ESCALE: [Self; 3]; + /// The polynomial `c0+c1*x+c2*x^2+c3*x^3` approximates `x^(1/3)` on `[1,2]` + /// with maximal error < 9.2e-5 (attained at x=2) + const C: [Self; 4]; + const U0: Self; + const U1: Self; + const RSCALE: [Self; 6]; + const OFF: [Self; 4]; + const WLIST: [(Self, Self); 7]; + const AZMAGIC1: Self; + const AZMAGIC2: Self; + const AZMAGIC3: Self; + const AZMAGIC4: Self; + fn fma(self, y: Self, z: Self) -> Self; } -fn fmaf64(x: f64, y: f64, z: f64) -> f64 { - #[cfg(intrinsics_enabled)] - { - return unsafe { core::intrinsics::fmaf64(x, y, z) }; +impl CbrtHelper for f64 { + const ESCALE: [Self; 3] = [ + 1.0, + hf64!("0x1.428a2f98d728bp+0"), /* 2^(1/3) */ + hf64!("0x1.965fea53d6e3dp+0"), /* 2^(2/3) */ + ]; + + const C: [Self; 4] = [ + // hf64!("0x1.1b850259b99ddp-1"), + // hf64!("0x1.2b9762efeffecp-1"), + // hf64!("-0x1.4af8eb64ea1ecp-3"), + // hf64!("0x1.7590cccfad50bp-6"), + hf64!("0x1.1b0babccfef9cp-1"), + hf64!("0x1.2c9a3e94d1da5p-1"), + hf64!("-0x1.4dc30b1a1ddbap-3"), + hf64!("0x1.7a8d3e4ec9b07p-6"), + ]; + + // 0.33333333... + const U0: Self = hf64!("0x1.5555555555555p-2"); + + // 0.22222222... + const U1: Self = hf64!("0x1.c71c71c71c71cp-3"); + + const RSCALE: [Self; 6] = [1.0, -1.0, 0.5, -0.5, 0.25, -0.25]; + + const OFF: [Self; 4] = [hf64!("0x1p-53"), 0.0, 0.0, 0.0]; + + const WLIST: [(Self, Self); 7] = [ + (hf64!("0x1.3a9ccd7f022dbp+0"), hf64!("0x1.1236160ba9b93p+0")), // ~ 0x1.1236160ba9b930000000000001e7e8fap+0 + (hf64!("0x1.7845d2faac6fep+0"), hf64!("0x1.23115e657e49cp+0")), // ~ 0x1.23115e657e49c0000000000001d7a799p+0 + (hf64!("0x1.d1ef81cbbbe71p+0"), hf64!("0x1.388fb44cdcf5ap+0")), // ~ 0x1.388fb44cdcf5a0000000000002202c55p+0 + (hf64!("0x1.0a2014f62987cp+1"), hf64!("0x1.46bcbf47dc1e8p+0")), // ~ 0x1.46bcbf47dc1e8000000000000303aa2dp+0 + (hf64!("0x1.fe18a044a5501p+1"), hf64!("0x1.95decfec9c904p+0")), // ~ 0x1.95decfec9c9040000000000000159e8ep+0 + (hf64!("0x1.a6bb8c803147bp+2"), hf64!("0x1.e05335a6401dep+0")), // ~ 0x1.e05335a6401de00000000000027ca017p+0 + (hf64!("0x1.ac8538a031cbdp+2"), hf64!("0x1.e281d87098de8p+0")), // ~ 0x1.e281d87098de80000000000000ee9314p+0 + ]; + + const AZMAGIC1: Self = hf64!("0x1.9b78223aa307cp+1"); + const AZMAGIC2: Self = hf64!("0x1.79d15d0e8d59cp+0"); + const AZMAGIC3: Self = hf64!("0x1.a202bfc89ddffp+2"); + const AZMAGIC4: Self = hf64!("0x1.de87aa837820fp+0"); + + fn fma(self, y: Self, z: Self) -> Self { + #[cfg(intrinsics_enabled)] + { + return unsafe { core::intrinsics::fmaf64(self, y, z) }; + } + + #[cfg(not(intrinsics_enabled))] + { + return super::fma(self, y, z); + } } +} - #[cfg(not(intrinsics_enabled))] - { - return super::fma(x, y, z); +#[cfg(f128_enabled)] +impl CbrtHelper for f128 { + const ESCALE: [Self; 3] = [ + 1.0, + hf128!("0x1.428a2f98d728acf826cc8664b665p+0"), /* 2^(1/3) */ + hf128!("0x1.965fea53d6e3c53be1ca3482bf3ap+0"), /* 2^(2/3) */ + ]; + + const C: [Self; 4] = [ + hf128!("0x1.1b850223b8bf644fcef50feeced1p-1"), + hf128!("0x1.2b97635e9e17d5240965cb56dc73p-1"), + hf128!("-0x1.4af8ec964bbc3767a6cf8ac456cbp-3"), + hf128!("0x1.7590ceecbb8c4c40d8c5e8b64d6bp-6"), + ]; + + const U0: Self = 0.3333333333333333333333333333333333333333; + + const U1: Self = 0.2222222222222222222222222222222222222222; + + const RSCALE: [Self; 6] = [1.0, -1.0, 0.5, -0.5, 0.25, -0.25]; + + const OFF: [Self; 4] = [hf128!("0x1p-53"), 0.0, 0.0, 0.0]; + + // Other rounding modes unsupported for f128 + const WLIST: [(Self, Self); 7] = + [(0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0), (0.0, 0.0)]; + + const AZMAGIC1: Self = hf128!("0x1.9b78223aa307cp+1"); + const AZMAGIC2: Self = hf128!("0x1.79d15d0e8d59cp+0"); + const AZMAGIC3: Self = hf128!("0x1.a202bfc89ddffp+2"); + const AZMAGIC4: Self = hf128!("0x1.de87aa837820fp+0"); + + fn fma(self, y: Self, z: Self) -> Self { + #[cfg(intrinsics_enabled)] + { + return unsafe { core::intrinsics::fmaf128(self, y, z) }; + } + + #[cfg(not(intrinsics_enabled))] + { + return super::fmaf128(self, y, z); + } } } diff --git a/src/math/mod.rs b/src/math/mod.rs index e58d79adc..2dfdd24e3 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -390,6 +390,7 @@ cfg_if! { mod truncf128; // verify-sorted-end + // pub use self::cbrt::cbrtf128; // verify-sorted-start pub use self::ceilf128::ceilf128; pub use self::copysignf128::copysignf128; diff --git a/src/math/support/float_traits.rs b/src/math/support/float_traits.rs index 42ce31484..7c90dc292 100644 --- a/src/math/support/float_traits.rs +++ b/src/math/support/float_traits.rs @@ -10,6 +10,7 @@ pub trait Float: + PartialEq + PartialOrd + ops::AddAssign + + ops::SubAssign + ops::MulAssign + ops::Add + ops::Sub @@ -43,6 +44,11 @@ pub trait Float: const MIN_POSITIVE_NORMAL: Self; + /// `2^sig_bits`, e.g. `0x1p-52` for `f64`. Used for normalization. + const TWO_POW_SIG_BITS: Self; + /// `2^-sig_bits`, e.g. `0x1p-52` for `f64`. Used for normalization. + const TWO_POW_NEG_SIG_BITS: Self; + /// The bitwidth of the float type const BITS: u32; @@ -205,6 +211,14 @@ macro_rules! float_impl { // Exponent is a 1 in the LSB const MIN_POSITIVE_NORMAL: Self = $from_bits(1 << Self::SIG_BITS); + /// `2^sig_bits` + const TWO_POW_SIG_BITS: Self = + $from_bits(((Self::SIG_BITS + Self::EXP_BIAS) as Self::Int) << Self::SIG_BITS); + /// `2^-sig_bits` + const TWO_POW_NEG_SIG_BITS: Self = $from_bits( + ((-(Self::SIG_BITS as i32) + Self::EXP_BIAS as i32) as Self::Int) << Self::SIG_BITS, + ); + const PI: Self = core::$ty::consts::PI; const NEG_PI: Self = -Self::PI; const FRAC_PI_2: Self = core::$ty::consts::FRAC_PI_2;