Skip to main content

trig_const/
floor.rs

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
10const SIG_BITS: u32 = 52;
11const BITS: u32 = 64;
12const EXP_BITS: u32 = BITS - SIG_BITS - 1;
13const EXP_SAT: u32 = (1 << EXP_BITS) - 1;
14const EXP_BIAS: u32 = EXP_SAT >> 1;
15const SIG_MASK: u64 = 4503599627370495;
16
17pub const fn floor(x: f64) -> f64 {
18    nightly_exp!(floor, floor_inner, x)
19}
20
21const fn floor_inner(x: f64) -> f64 {
22    let zero = 0;
23
24    let mut ix = x.to_bits();
25    let e = exp_unbiased(x);
26
27    // If the represented value has no fractional part, no truncation is needed.
28    if e >= SIG_BITS as i32 {
29        return x;
30    }
31
32    if e >= 0 {
33        // |x| >= 1.0
34        let m = SIG_MASK >> unsigned(e);
35        if ix & m == zero {
36            // Portion to be masked is already zero; no adjustment needed.
37            return x;
38        }
39
40        if x.is_sign_negative() {
41            ix += m;
42        }
43
44        ix &= !m;
45        f64::from_bits(ix)
46    } else if x.is_sign_positive() {
47        // 0.0 <= x < 1.0; rounding down goes toward +0.0.
48        0.0
49    } else if ix << 1 != zero {
50        // -1.0 < x < 0.0; rounding down goes toward -1.0.
51        -1.0
52    } else {
53        // -0.0 remains unchanged
54        x
55    }
56}
57
58const fn ex(x: f64) -> u32 {
59    (x.to_bits() >> SIG_BITS) as u32 & EXP_SAT
60}
61
62const fn signed(x: u32) -> i32 {
63    x as i32
64}
65
66const fn unsigned(x: i32) -> u32 {
67    x as u32
68}
69
70const fn exp_unbiased(x: f64) -> i32 {
71    signed(ex(x)) - EXP_BIAS as i32
72}