Skip to content

Commit 11ff0e1

Browse files
committed
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]
1 parent b5cdbd4 commit 11ff0e1

File tree

4 files changed

+91
-91
lines changed

4 files changed

+91
-91
lines changed

src/math/ceil.rs

+1-41
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
#![allow(unreachable_code)]
2-
use core::f64;
3-
4-
const TOINT: f64 = 1. / f64::EPSILON;
5-
61
/// Ceil (f64)
72
///
83
/// Finds the nearest integer greater than or equal to `x`.
@@ -15,40 +10,5 @@ pub fn ceil(x: f64) -> f64 {
1510
args: x,
1611
}
1712

18-
let u: u64 = x.to_bits();
19-
let e: i64 = ((u >> 52) & 0x7ff) as i64;
20-
let y: f64;
21-
22-
if e >= 0x3ff + 52 || x == 0. {
23-
return x;
24-
}
25-
// y = int(x) - x, where int(x) is an integer neighbor of x
26-
y = if (u >> 63) != 0 { x - TOINT + TOINT - x } else { x + TOINT - TOINT - x };
27-
// special case because of non-nearest rounding modes
28-
if e < 0x3ff {
29-
force_eval!(y);
30-
return if (u >> 63) != 0 { -0. } else { 1. };
31-
}
32-
if y < 0. { x + y + 1. } else { x + y }
33-
}
34-
35-
#[cfg(test)]
36-
mod tests {
37-
use super::*;
38-
39-
#[test]
40-
fn sanity_check() {
41-
assert_eq!(ceil(1.1), 2.0);
42-
assert_eq!(ceil(2.9), 3.0);
43-
}
44-
45-
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
46-
#[test]
47-
fn spec_tests() {
48-
// Not Asserted: that the current rounding mode has no effect.
49-
assert!(ceil(f64::NAN).is_nan());
50-
for f in [0.0, -0.0, f64::INFINITY, f64::NEG_INFINITY].iter().copied() {
51-
assert_eq!(ceil(f), f);
52-
}
53-
}
13+
super::generic::ceil(x)
5414
}

src/math/ceilf.rs

+1-50
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use core::f32;
2-
31
/// Ceil (f32)
42
///
53
/// Finds the nearest integer greater than or equal to `x`.
@@ -11,52 +9,5 @@ pub fn ceilf(x: f32) -> f32 {
119
args: x,
1210
}
1311

14-
let mut ui = x.to_bits();
15-
let e = (((ui >> 23) & 0xff).wrapping_sub(0x7f)) as i32;
16-
17-
if e >= 23 {
18-
return x;
19-
}
20-
if e >= 0 {
21-
let m = 0x007fffff >> e;
22-
if (ui & m) == 0 {
23-
return x;
24-
}
25-
force_eval!(x + f32::from_bits(0x7b800000));
26-
if ui >> 31 == 0 {
27-
ui += m;
28-
}
29-
ui &= !m;
30-
} else {
31-
force_eval!(x + f32::from_bits(0x7b800000));
32-
if ui >> 31 != 0 {
33-
return -0.0;
34-
} else if ui << 1 != 0 {
35-
return 1.0;
36-
}
37-
}
38-
f32::from_bits(ui)
39-
}
40-
41-
// PowerPC tests are failing on LLVM 13: https://github.com/rust-lang/rust/issues/88520
42-
#[cfg(not(target_arch = "powerpc64"))]
43-
#[cfg(test)]
44-
mod tests {
45-
use super::*;
46-
47-
#[test]
48-
fn sanity_check() {
49-
assert_eq!(ceilf(1.1), 2.0);
50-
assert_eq!(ceilf(2.9), 3.0);
51-
}
52-
53-
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
54-
#[test]
55-
fn spec_tests() {
56-
// Not Asserted: that the current rounding mode has no effect.
57-
assert!(ceilf(f32::NAN).is_nan());
58-
for f in [0.0, -0.0, f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
59-
assert_eq!(ceilf(f), f);
60-
}
61-
}
12+
super::generic::ceil(x)
6213
}

src/math/generic/ceil.rs

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* SPDX-License-Identifier: MIT */
2+
/* origin: musl src/math/ceilf.c */
3+
4+
//! Generic `ceil` algorithm.
5+
//!
6+
//! Note that this uses the algorithm from musl's `ceilf` rather than `ceil` or `ceill` because
7+
//! performance seems to be better (based on icount) and it does not seem to experience rounding
8+
//! errors on i386.
9+
10+
use super::super::{Float, Int, IntTy, MinInt};
11+
12+
pub fn ceil<F: Float>(x: F) -> F {
13+
let zero = IntTy::<F>::ZERO;
14+
15+
let mut ix = x.to_bits();
16+
let e = x.exp_unbiased();
17+
18+
// If the represented value has no fractional part, no truncation is needed.
19+
if e >= F::SIG_BITS as i32 {
20+
return x;
21+
}
22+
23+
if e >= 0 {
24+
// |x| >= 1.0
25+
26+
let m = F::SIG_MASK >> e.unsigned();
27+
if (ix & m) == zero {
28+
// Portion to be masked is already zero; no adjustment needed.
29+
return x;
30+
}
31+
32+
// Otherwise, raise an inexact exception.
33+
force_eval!(x + F::MAX);
34+
if x.is_sign_positive() {
35+
ix += m;
36+
}
37+
ix &= !m;
38+
} else {
39+
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
40+
force_eval!(x + F::MAX);
41+
42+
if x.is_sign_negative() {
43+
// -1.0 < x <= -0.0; rounding up goes toward -0.0.
44+
return F::NEG_ZERO;
45+
} else if ix << 1 != zero {
46+
// 0.0 < x < 1.0; rounding up goes toward +1.0.
47+
return F::ONE;
48+
}
49+
}
50+
51+
F::from_bits(ix)
52+
}
53+
54+
#[cfg(test)]
55+
mod tests {
56+
use super::*;
57+
58+
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
59+
fn spec_test<F: Float>() {
60+
// Not Asserted: that the current rounding mode has no effect.
61+
for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() {
62+
assert_biteq!(ceil(f), f);
63+
}
64+
}
65+
66+
#[test]
67+
fn sanity_check_f32() {
68+
assert_eq!(ceil(1.1f32), 2.0);
69+
assert_eq!(ceil(2.9f32), 3.0);
70+
}
71+
72+
#[test]
73+
fn spec_tests_f32() {
74+
spec_test::<f32>();
75+
}
76+
77+
#[test]
78+
fn sanity_check_f64() {
79+
assert_eq!(ceil(1.1f64), 2.0);
80+
assert_eq!(ceil(2.9f64), 3.0);
81+
}
82+
83+
#[test]
84+
fn spec_tests_f64() {
85+
spec_test::<f64>();
86+
}
87+
}

src/math/generic/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
mod ceil;
12
mod copysign;
23
mod fabs;
34
mod fdim;
45
mod sqrt;
56
mod trunc;
67

8+
pub use ceil::ceil;
79
pub use copysign::copysign;
810
pub use fabs::fabs;
911
pub use fdim::fdim;

0 commit comments

Comments
 (0)