From c00f1191666e6803fdd61eca2ac5f0cb07360656 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Mon, 13 Jan 2025 11:17:03 +0000 Subject: [PATCH] Add a generic version of `ceil` Additionally, make use of this version to implement `ceil` and `ceilf`. Musl's `ceilf` algorithm seems to work better for all versions of the functions. Testing with a generic version of musl's `ceil` routine showed the following regressions: icount::icount_bench_ceil_group::icount_bench_ceil logspace:setup_ceil() Performance has regressed: Instructions (14064 > 13171) regressed by +6.78005% (>+5.00000) Baselines: softfloat|softfloat Instructions: 14064|13171 (+6.78005%) [+1.06780x] L1 Hits: 16697|15803 (+5.65715%) [+1.05657x] L2 Hits: 0|0 (No change) RAM Hits: 7|8 (-12.5000%) [-1.14286x] Total read+write: 16704|15811 (+5.64797%) [+1.05648x] Estimated Cycles: 16942|16083 (+5.34104%) [+1.05341x] icount::icount_bench_ceilf_group::icount_bench_ceilf logspace:setup_ceilf() Performance has regressed: Instructions (14732 > 9901) regressed by +48.7931% (>+5.00000) Baselines: softfloat|softfloat Instructions: 14732|9901 (+48.7931%) [+1.48793x] L1 Hits: 17494|12611 (+38.7202%) [+1.38720x] L2 Hits: 0|0 (No change) RAM Hits: 6|6 (No change) Total read+write: 17500|12617 (+38.7018%) [+1.38702x] Estimated Cycles: 17704|12821 (+38.0860%) [+1.38086x] --- .../compiler-builtins/libm/src/math/ceil.rs | 42 +-------- .../compiler-builtins/libm/src/math/ceilf.rs | 51 +---------- .../libm/src/math/generic/ceil.rs | 87 +++++++++++++++++++ .../libm/src/math/generic/mod.rs | 2 + 4 files changed, 91 insertions(+), 91 deletions(-) create mode 100644 library/compiler-builtins/libm/src/math/generic/ceil.rs diff --git a/library/compiler-builtins/libm/src/math/ceil.rs b/library/compiler-builtins/libm/src/math/ceil.rs index 398bfee47a21..535f434ac243 100644 --- a/library/compiler-builtins/libm/src/math/ceil.rs +++ b/library/compiler-builtins/libm/src/math/ceil.rs @@ -1,8 +1,3 @@ -#![allow(unreachable_code)] -use core::f64; - -const TOINT: f64 = 1. / f64::EPSILON; - /// Ceil (f64) /// /// Finds the nearest integer greater than or equal to `x`. @@ -15,40 +10,5 @@ pub fn ceil(x: f64) -> f64 { args: x, } - let u: u64 = x.to_bits(); - let e: i64 = ((u >> 52) & 0x7ff) as i64; - let y: f64; - - if e >= 0x3ff + 52 || x == 0. { - return x; - } - // y = int(x) - x, where int(x) is an integer neighbor of x - y = if (u >> 63) != 0 { x - TOINT + TOINT - x } else { x + TOINT - TOINT - x }; - // special case because of non-nearest rounding modes - if e < 0x3ff { - force_eval!(y); - return if (u >> 63) != 0 { -0. } else { 1. }; - } - if y < 0. { x + y + 1. } else { x + y } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sanity_check() { - assert_eq!(ceil(1.1), 2.0); - assert_eq!(ceil(2.9), 3.0); - } - - /// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil - #[test] - fn spec_tests() { - // Not Asserted: that the current rounding mode has no effect. - assert!(ceil(f64::NAN).is_nan()); - for f in [0.0, -0.0, f64::INFINITY, f64::NEG_INFINITY].iter().copied() { - assert_eq!(ceil(f), f); - } - } + super::generic::ceil(x) } diff --git a/library/compiler-builtins/libm/src/math/ceilf.rs b/library/compiler-builtins/libm/src/math/ceilf.rs index 9e8e78e3e532..66d44189c921 100644 --- a/library/compiler-builtins/libm/src/math/ceilf.rs +++ b/library/compiler-builtins/libm/src/math/ceilf.rs @@ -1,5 +1,3 @@ -use core::f32; - /// Ceil (f32) /// /// Finds the nearest integer greater than or equal to `x`. @@ -11,52 +9,5 @@ pub fn ceilf(x: f32) -> f32 { args: x, } - let mut ui = x.to_bits(); - let e = (((ui >> 23) & 0xff).wrapping_sub(0x7f)) as i32; - - if e >= 23 { - return x; - } - if e >= 0 { - let m = 0x007fffff >> e; - if (ui & m) == 0 { - return x; - } - force_eval!(x + f32::from_bits(0x7b800000)); - if ui >> 31 == 0 { - ui += m; - } - ui &= !m; - } else { - force_eval!(x + f32::from_bits(0x7b800000)); - if ui >> 31 != 0 { - return -0.0; - } else if ui << 1 != 0 { - return 1.0; - } - } - f32::from_bits(ui) -} - -// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520 -#[cfg(not(target_arch = "powerpc64"))] -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn sanity_check() { - assert_eq!(ceilf(1.1), 2.0); - assert_eq!(ceilf(2.9), 3.0); - } - - /// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil - #[test] - fn spec_tests() { - // Not Asserted: that the current rounding mode has no effect. - assert!(ceilf(f32::NAN).is_nan()); - for f in [0.0, -0.0, f32::INFINITY, f32::NEG_INFINITY].iter().copied() { - assert_eq!(ceilf(f), f); - } - } + super::generic::ceil(x) } diff --git a/library/compiler-builtins/libm/src/math/generic/ceil.rs b/library/compiler-builtins/libm/src/math/generic/ceil.rs new file mode 100644 index 000000000000..d16d0657279e --- /dev/null +++ b/library/compiler-builtins/libm/src/math/generic/ceil.rs @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: MIT */ +/* origin: musl src/math/ceilf.c */ + +//! Generic `ceil` algorithm. +//! +//! Note that this uses the algorithm from musl's `ceilf` rather than `ceil` or `ceill` because +//! performance seems to be better (based on icount) and it does not seem to experience rounding +//! errors on i386. + +use super::super::{Float, Int, IntTy, MinInt}; + +pub fn ceil(x: F) -> F { + let zero = IntTy::::ZERO; + + let mut ix = x.to_bits(); + let e = x.exp_unbiased(); + + // If the represented value has no fractional part, no truncation is needed. + if e >= F::SIG_BITS as i32 { + return x; + } + + if e >= 0 { + // |x| >= 1.0 + + let m = F::SIG_MASK >> e.unsigned(); + if (ix & m) == zero { + // Portion to be masked is already zero; no adjustment needed. + return x; + } + + // Otherwise, raise an inexact exception. + force_eval!(x + F::MAX); + if x.is_sign_positive() { + ix += m; + } + ix &= !m; + } else { + // |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0). + force_eval!(x + F::MAX); + + if x.is_sign_negative() { + // -1.0 < x <= -0.0; rounding up goes toward -0.0. + return F::NEG_ZERO; + } else if ix << 1 != zero { + // 0.0 < x < 1.0; rounding up goes toward +1.0. + return F::ONE; + } + } + + F::from_bits(ix) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil + fn spec_test() { + // Not Asserted: that the current rounding mode has no effect. + for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() { + assert_biteq!(ceil(f), f); + } + } + + #[test] + fn sanity_check_f32() { + assert_eq!(ceil(1.1f32), 2.0); + assert_eq!(ceil(2.9f32), 3.0); + } + + #[test] + fn spec_tests_f32() { + spec_test::(); + } + + #[test] + fn sanity_check_f64() { + assert_eq!(ceil(1.1f64), 2.0); + assert_eq!(ceil(2.9f64), 3.0); + } + + #[test] + fn spec_tests_f64() { + spec_test::(); + } +} diff --git a/library/compiler-builtins/libm/src/math/generic/mod.rs b/library/compiler-builtins/libm/src/math/generic/mod.rs index 3b5a2c3effd7..f8bb9fa6adb1 100644 --- a/library/compiler-builtins/libm/src/math/generic/mod.rs +++ b/library/compiler-builtins/libm/src/math/generic/mod.rs @@ -1,9 +1,11 @@ +mod ceil; mod copysign; mod fabs; mod fdim; mod sqrt; mod trunc; +pub use ceil::ceil; pub use copysign::copysign; pub use fabs::fabs; pub use fdim::fdim;