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]
This commit is contained in:
parent
a7cd13b6a3
commit
c00f119166
4 changed files with 91 additions and 91 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
87
library/compiler-builtins/libm/src/math/generic/ceil.rs
Normal file
87
library/compiler-builtins/libm/src/math/generic/ceil.rs
Normal file
|
|
@ -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<F: Float>(x: F) -> F {
|
||||
let zero = IntTy::<F>::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<F: Float>() {
|
||||
// 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::<f32>();
|
||||
}
|
||||
|
||||
#[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::<f64>();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue