Skip to content

Commit 5cfb028

Browse files
committed
Try to win perf back
experiment try just using exp_unbiased experiment Fix experiment Update
1 parent 9c7b4e2 commit 5cfb028

File tree

4 files changed

+60
-41
lines changed

4 files changed

+60
-41
lines changed

crates/libm-test/benches/icount.rs

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ main!(
6969
icount_bench_cbrt_group,
7070
icount_bench_cbrtf_group,
7171
icount_bench_ceil_group,
72+
icount_bench_ceilf128_group,
73+
icount_bench_ceilf16_group,
7274
icount_bench_ceilf_group,
7375
icount_bench_copysign_group,
7476
icount_bench_copysignf128_group,

src/math/generic/ceil.rs

+54-37
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,65 @@
1-
/* SPDX-License-Identifier: MIT
2-
* origin: musl src/math/ceil.c */
1+
/* SPDX-License-Identifier: MIT */
2+
/* origin: musl src/math/ceil.c */
33

4-
use super::super::{CastInto, Float};
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};
511

612
pub fn ceil<F: Float>(x: F) -> F {
7-
let toint = F::ONE / F::EPSILON;
13+
let zero = IntTy::<F>::ZERO;
814

9-
// NB: using `exp` here and comparing to values adjusted by `EXP_BIAS` has better
10-
// perf than using `exp_unbiased` here.
11-
let e = x.exp();
12-
let y: F;
15+
let mut ix = x.to_bits();
16+
let e = x.exp_unbiased();
1317

1418
// If the represented value has no fractional part, no truncation is needed.
15-
if e >= (F::SIG_BITS + F::EXP_BIAS).cast() || x == F::ZERO {
19+
if e >= F::SIG_BITS as i32 {
1620
return x;
1721
}
1822

19-
let neg = x.is_sign_negative();
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+
}
2031

21-
// y = int(x) - x, where int(x) is an integer neighbor of x.
22-
// The `x - t + t - x` method is a way to expose non-round-to-even modes.
23-
y = if neg { x - toint + toint - x } else { x + toint - toint - x };
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);
2441

25-
// Exp < 0; special case because of non-nearest rounding modes
26-
if e < F::EXP_BIAS.cast() {
27-
// Raise `FE_INEXACT`
28-
force_eval!(y);
29-
return if neg { F::NEG_ZERO } else { F::ONE };
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+
}
3049
}
3150

32-
if y < F::ZERO { x + y + F::ONE } else { x + y }
51+
F::from_bits(ix)
3352
}
3453

3554
#[cfg(test)]
3655
mod tests {
3756
use super::*;
3857

39-
#[test]
40-
fn sanity_check_f64() {
41-
assert_eq!(ceil(1.1f64), 2.0);
42-
assert_eq!(ceil(2.9f64), 3.0);
43-
}
44-
45-
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
46-
#[test]
47-
fn spec_tests_f64() {
58+
/// Test against https://en.cppreference.com/w/cpp/numeric/math/ceil
59+
fn spec_test<F: Float>() {
4860
// 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);
61+
for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() {
62+
assert_biteq!(ceil(f), f);
5263
}
5364
}
5465

@@ -58,13 +69,19 @@ mod tests {
5869
assert_eq!(ceil(2.9f32), 3.0);
5970
}
6071

61-
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/ceil
6272
#[test]
6373
fn spec_tests_f32() {
64-
// Not Asserted: that the current rounding mode has no effect.
65-
assert!(ceil(f32::NAN).is_nan());
66-
for f in [0.0, -0.0, f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
67-
assert_eq!(ceil(f), f);
68-
}
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>();
6986
}
7087
}

src/math/generic/sqrt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ where
9696
ix = scaled.to_bits();
9797
match top {
9898
Exp::Shifted(ref mut v) => {
99-
*v = scaled.exp().unsigned();
99+
*v = scaled.exp();
100100
*v = (*v).wrapping_sub(F::SIG_BITS);
101101
}
102102
Exp::NoShift(()) => {

src/math/support/float_traits.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -108,13 +108,13 @@ pub trait Float:
108108
}
109109

110110
/// Returns the exponent, not adjusting for bias, not accounting for subnormals or zero.
111-
fn exp(self) -> i32 {
112-
(u32::cast_from(self.to_bits() >> Self::SIG_BITS) & Self::EXP_MAX).signed()
111+
fn exp(self) -> u32 {
112+
u32::cast_from(self.to_bits() >> Self::SIG_BITS) & Self::EXP_MAX
113113
}
114114

115115
/// Extract the exponent and adjust it for bias, not accounting for subnormals or zero.
116116
fn exp_unbiased(self) -> i32 {
117-
self.exp() - (Self::EXP_BIAS as i32)
117+
self.exp().signed() - (Self::EXP_BIAS as i32)
118118
}
119119

120120
/// Returns the significand with no implicit bit (or the "fractional" part)

0 commit comments

Comments
 (0)