Skip to content

Commit 42647c6

Browse files
committed
Add a generic version of floor
Additionally, make use of this version to implement `floor` and `floorf`. Similar to `ceil`, musl'f `ceilf` routine seems to work better for all float widths than the `ceil` algorithm. Trying with the `ceil` (`f64`) algorithm produced the following regressions: icount::icount_bench_floor_group::icount_bench_floor logspace:setup_floor() Performance has regressed: Instructions (14064 > 13171) regressed by +6.78005% (>+5.00000) Baselines: softfloat|softfloat Instructions: 14064|13171 (+6.78005%) [+1.06780x] L1 Hits: 16821|15802 (+6.44855%) [+1.06449x] L2 Hits: 0|0 (No change) RAM Hits: 8|9 (-11.1111%) [-1.12500x] Total read+write: 16829|15811 (+6.43856%) [+1.06439x] Estimated Cycles: 17101|16117 (+6.10535%) [+1.06105x] icount::icount_bench_floorf128_group::icount_bench_floorf128 logspace:setup_floorf128() Baselines: softfloat|softfloat Instructions: 166868|N/A (*********) L1 Hits: 221429|N/A (*********) L2 Hits: 1|N/A (*********) RAM Hits: 34|N/A (*********) Total read+write: 221464|N/A (*********) Estimated Cycles: 222624|N/A (*********) icount::icount_bench_floorf16_group::icount_bench_floorf16 logspace:setup_floorf16() Baselines: softfloat|softfloat Instructions: 143029|N/A (*********) L1 Hits: 176517|N/A (*********) L2 Hits: 1|N/A (*********) RAM Hits: 13|N/A (*********) Total read+write: 176531|N/A (*********) Estimated Cycles: 176977|N/A (*********) icount::icount_bench_floorf_group::icount_bench_floorf logspace:setup_floorf() Performance has regressed: Instructions (14732 > 10441) regressed by +41.0976% (>+5.00000) Baselines: softfloat|softfloat Instructions: 14732|10441 (+41.0976%) [+1.41098x] L1 Hits: 17616|13027 (+35.2268%) [+1.35227x] L2 Hits: 0|0 (No change) RAM Hits: 8|6 (+33.3333%) [+1.33333x] Total read+write: 17624|13033 (+35.2260%) [+1.35226x] Estimated Cycles: 17896|13237 (+35.1968%) [+1.35197x]
1 parent 812e15a commit 42647c6

File tree

5 files changed

+114
-93
lines changed

5 files changed

+114
-93
lines changed

etc/function-definitions.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,16 @@
336336
"src/libm_helper.rs",
337337
"src/math/arch/i586.rs",
338338
"src/math/arch/wasm32.rs",
339-
"src/math/floor.rs"
339+
"src/math/floor.rs",
340+
"src/math/generic/floor.rs"
340341
],
341342
"type": "f64"
342343
},
343344
"floorf": {
344345
"sources": [
345346
"src/math/arch/wasm32.rs",
346-
"src/math/floorf.rs"
347+
"src/math/floorf.rs",
348+
"src/math/generic/floor.rs"
347349
],
348350
"type": "f32"
349351
},

src/math/floor.rs

Lines changed: 1 addition & 40 deletions
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
/// Floor (f64)
72
///
83
/// Finds the nearest integer less than or equal to `x`.
@@ -15,39 +10,5 @@ pub fn floor(x: f64) -> f64 {
1510
args: x,
1611
}
1712

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

src/math/floorf.rs

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use core::f32;
2-
31
/// Floor (f32)
42
///
53
/// Finds the nearest integer less than or equal to `x`.
@@ -11,53 +9,5 @@ pub fn floorf(x: f32) -> f32 {
119
args: x,
1210
}
1311

14-
let mut ui = x.to_bits();
15-
let e = (((ui >> 23) as i32) & 0xff) - 0x7f;
16-
17-
if e >= 23 {
18-
return x;
19-
}
20-
if e >= 0 {
21-
let m: u32 = 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-
ui = 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!(floorf(0.5), 0.0);
50-
assert_eq!(floorf(1.1), 1.0);
51-
assert_eq!(floorf(2.9), 2.0);
52-
}
53-
54-
/// The spec: https://en.cppreference.com/w/cpp/numeric/math/floor
55-
#[test]
56-
fn spec_tests() {
57-
// Not Asserted: that the current rounding mode has no effect.
58-
assert!(floorf(f32::NAN).is_nan());
59-
for f in [0.0, -0.0, f32::INFINITY, f32::NEG_INFINITY].iter().copied() {
60-
assert_eq!(floorf(f), f);
61-
}
62-
}
12+
return super::generic::floor(x);
6313
}

src/math/generic/floor.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* SPDX-License-Identifier: MIT
2+
* origin: musl src/math/floor.c */
3+
4+
//! Generic `floor` algorithm.
5+
//!
6+
//! Note that this uses the algorithm from musl's `floorf` rather than `floor` or `floorl` 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 floor<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+
35+
if x.is_sign_negative() {
36+
ix += m;
37+
}
38+
39+
ix &= !m;
40+
F::from_bits(ix)
41+
} else {
42+
// |x| < 1.0, raise an inexact exception since truncation will happen (unless x == 0).
43+
force_eval!(x + F::MAX);
44+
45+
if x.is_sign_positive() {
46+
// 0.0 <= x < 1.0; rounding down goes toward +0.0.
47+
F::ZERO
48+
} else if ix << 1 != zero {
49+
// -1.0 < x < 0.0; rounding down goes toward -1.0.
50+
F::NEG_ONE
51+
} else {
52+
// -0.0 remains unchanged
53+
x
54+
}
55+
}
56+
}
57+
58+
#[cfg(test)]
59+
mod tests {
60+
use super::*;
61+
62+
/// Test against https://en.cppreference.com/w/cpp/numeric/math/floor
63+
fn spec_test<F: Float>() {
64+
// Not Asserted: that the current rounding mode has no effect.
65+
for f in [F::ZERO, F::NEG_ZERO, F::INFINITY, F::NEG_INFINITY].iter().copied() {
66+
assert_biteq!(floor(f), f);
67+
}
68+
}
69+
70+
/* Skipping f16 / f128 "sanity_check"s due to rejected literal lexing at MSRV */
71+
72+
#[test]
73+
#[cfg(f16_enabled)]
74+
fn spec_tests_f16() {
75+
spec_test::<f16>();
76+
}
77+
78+
#[test]
79+
fn sanity_check_f32() {
80+
assert_eq!(floor(0.5f32), 0.0);
81+
assert_eq!(floor(1.1f32), 1.0);
82+
assert_eq!(floor(2.9f32), 2.0);
83+
}
84+
85+
#[test]
86+
fn spec_tests_f32() {
87+
spec_test::<f32>();
88+
}
89+
90+
#[test]
91+
fn sanity_check_f64() {
92+
assert_eq!(floor(1.1f64), 1.0);
93+
assert_eq!(floor(2.9f64), 2.0);
94+
}
95+
96+
#[test]
97+
fn spec_tests_f64() {
98+
spec_test::<f64>();
99+
}
100+
101+
#[test]
102+
#[cfg(f128_enabled)]
103+
fn spec_tests_f128() {
104+
spec_test::<f128>();
105+
}
106+
}

src/math/generic/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ mod ceil;
22
mod copysign;
33
mod fabs;
44
mod fdim;
5+
mod floor;
56
mod sqrt;
67
mod trunc;
78

89
pub use ceil::ceil;
910
pub use copysign::copysign;
1011
pub use fabs::fabs;
1112
pub use fdim::fdim;
13+
pub use floor::floor;
1214
pub use sqrt::sqrt;
1315
pub use trunc::trunc;

0 commit comments

Comments
 (0)