From 6ac06a97e591f3373b16859756567901ff5056fb Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Sat, 11 Jan 2025 23:38:19 +0000 Subject: [PATCH] Add a generic version of `trunc` The algorithm is identical for both types, so this is a straightforward routine to port. --- .../libm/src/math/generic/mod.rs | 2 + .../libm/src/math/generic/trunc.rs | 54 +++++++++++++++++++ .../compiler-builtins/libm/src/math/mod.rs | 2 +- .../compiler-builtins/libm/src/math/trunc.rs | 30 +---------- .../compiler-builtins/libm/src/math/truncf.rs | 22 +------- 5 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 library/compiler-builtins/libm/src/math/generic/trunc.rs diff --git a/library/compiler-builtins/libm/src/math/generic/mod.rs b/library/compiler-builtins/libm/src/math/generic/mod.rs index 08524b685e25..e5166ca10eb3 100644 --- a/library/compiler-builtins/libm/src/math/generic/mod.rs +++ b/library/compiler-builtins/libm/src/math/generic/mod.rs @@ -1,5 +1,7 @@ mod copysign; mod fabs; +mod trunc; pub use copysign::copysign; pub use fabs::fabs; +pub use trunc::trunc; diff --git a/library/compiler-builtins/libm/src/math/generic/trunc.rs b/library/compiler-builtins/libm/src/math/generic/trunc.rs new file mode 100644 index 000000000000..5d0ba6109afa --- /dev/null +++ b/library/compiler-builtins/libm/src/math/generic/trunc.rs @@ -0,0 +1,54 @@ +use super::super::{Float, Int, IntTy, MinInt}; + +pub fn trunc(x: F) -> F { + let mut xi: F::Int = x.to_bits(); + let e: i32 = x.exp_unbiased(); + + // C1: The represented value has no fractional part, so no truncation is needed + if e >= F::SIG_BITS as i32 { + return x; + } + + let mask = if e < 0 { + // C2: If the exponent is negative, the result will be zero so we mask out everything + // except the sign. + F::SIGN_MASK + } else { + // C3: Otherwise, we mask out the last `e` bits of the significand. + !(F::SIG_MASK >> e.unsigned()) + }; + + // C4: If the to-be-masked-out portion is already zero, we have an exact result + if (xi & !mask) == IntTy::::ZERO { + return x; + } + + // C5: Otherwise the result is inexact and we will truncate. Raise `FE_INEXACT`, mask the + // result, and return. + force_eval!(x + F::MAX); + xi &= mask; + F::from_bits(xi) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sanity_check() { + assert_biteq!(trunc(1.1f32), 1.0); + assert_biteq!(trunc(1.1f64), 1.0); + + // C1 + assert_biteq!(trunc(hf32!("0x1p23")), hf32!("0x1p23")); + assert_biteq!(trunc(hf64!("0x1p52")), hf64!("0x1p52")); + assert_biteq!(trunc(hf32!("-0x1p23")), hf32!("-0x1p23")); + assert_biteq!(trunc(hf64!("-0x1p52")), hf64!("-0x1p52")); + + // C2 + assert_biteq!(trunc(hf32!("0x1p-1")), 0.0); + assert_biteq!(trunc(hf64!("0x1p-1")), 0.0); + assert_biteq!(trunc(hf32!("-0x1p-1")), -0.0); + assert_biteq!(trunc(hf64!("-0x1p-1")), -0.0); + } +} diff --git a/library/compiler-builtins/libm/src/math/mod.rs b/library/compiler-builtins/libm/src/math/mod.rs index 5baf35e42453..c0d038a0c67f 100644 --- a/library/compiler-builtins/libm/src/math/mod.rs +++ b/library/compiler-builtins/libm/src/math/mod.rs @@ -121,7 +121,7 @@ use self::rem_pio2::rem_pio2; use self::rem_pio2_large::rem_pio2_large; use self::rem_pio2f::rem_pio2f; #[allow(unused_imports)] -use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, MinInt}; +use self::support::{CastFrom, CastInto, DInt, Float, HInt, Int, IntTy, MinInt}; // Public modules mod acos; diff --git a/library/compiler-builtins/libm/src/math/trunc.rs b/library/compiler-builtins/libm/src/math/trunc.rs index 7e5c4f2c2ae5..2cc8aaa7e8f9 100644 --- a/library/compiler-builtins/libm/src/math/trunc.rs +++ b/library/compiler-builtins/libm/src/math/trunc.rs @@ -1,5 +1,3 @@ -use core::f64; - /// Rounds the number toward 0 to the closest integral value (f64). /// /// This effectively removes the decimal part of the number, leaving the integral part. @@ -11,31 +9,5 @@ pub fn trunc(x: f64) -> f64 { args: x, } - let x1p120 = f64::from_bits(0x4770000000000000); // 0x1p120f === 2 ^ 120 - - let mut i: u64 = x.to_bits(); - let mut e: i64 = ((i >> 52) & 0x7ff) as i64 - 0x3ff + 12; - let m: u64; - - if e >= 52 + 12 { - return x; - } - if e < 12 { - e = 1; - } - m = -1i64 as u64 >> e; - if (i & m) == 0 { - return x; - } - force_eval!(x + x1p120); - i &= !m; - f64::from_bits(i) -} - -#[cfg(test)] -mod tests { - #[test] - fn sanity_check() { - assert_eq!(super::trunc(1.1), 1.0); - } + super::generic::trunc(x) } diff --git a/library/compiler-builtins/libm/src/math/truncf.rs b/library/compiler-builtins/libm/src/math/truncf.rs index b491747d999e..14533a267063 100644 --- a/library/compiler-builtins/libm/src/math/truncf.rs +++ b/library/compiler-builtins/libm/src/math/truncf.rs @@ -1,5 +1,3 @@ -use core::f32; - /// Rounds the number toward 0 to the closest integral value (f32). /// /// This effectively removes the decimal part of the number, leaving the integral part. @@ -11,25 +9,7 @@ pub fn truncf(x: f32) -> f32 { args: x, } - let x1p120 = f32::from_bits(0x7b800000); // 0x1p120f === 2 ^ 120 - - let mut i: u32 = x.to_bits(); - let mut e: i32 = ((i >> 23) & 0xff) as i32 - 0x7f + 9; - let m: u32; - - if e >= 23 + 9 { - return x; - } - if e < 9 { - e = 1; - } - m = -1i32 as u32 >> e; - if (i & m) == 0 { - return x; - } - force_eval!(x + x1p120); - i &= !m; - f32::from_bits(i) + super::generic::trunc(x) } // PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520