Skip to main content

deep_time/math/
round.rs

1use super::trunc;
2use crate::Real;
3
4/// Const-compatible version of `f64::round` (rounds to the nearest integer).
5///
6/// Half-way cases round away from zero (`0.5` → `1.0`, `-0.5` → `-1.0`).
7/// Values with magnitude ≥ 2^52 are returned unchanged (already integral at this precision).
8pub const fn round(x: Real) -> Real {
9    const THRESHOLD: Real = (1u64 << 52) as Real;
10
11    if x >= 0.0 {
12        if x >= THRESHOLD { x } else { trunc(x + 0.5) }
13    } else if x <= -THRESHOLD {
14        x
15    } else {
16        trunc(x - 0.5)
17    }
18}
19
20#[cfg(feature = "std")]
21#[cfg(test)]
22mod tests {
23    use super::round;
24
25    #[test]
26    fn round_matches_std_round() {
27        let cases: &[f64] = &[
28            0.0,
29            -0.0,
30            0.1,
31            -0.1,
32            0.4,
33            -0.4,
34            0.5,
35            -0.5,
36            0.6,
37            -0.6,
38            1.3,
39            -1.3,
40            1.5,
41            -1.5,
42            2.5,
43            -2.5,
44            123.456,
45            -123.456,
46            4503599627370495.5, // just below 2^52
47            4503599627370496.0, // exactly 2^52
48            4503599627370496.3,
49            9007199254740992.0, // 2^53
50            f64::INFINITY,
51            f64::NEG_INFINITY,
52        ];
53
54        for &x in cases {
55            let expected = x.round();
56            let got = round(x);
57            assert_eq!(got, expected, "round({}) mismatch", x);
58        }
59
60        // NaN must be handled separately because NaN != NaN
61        assert!(round(f64::NAN).is_nan());
62        assert!(f64::NAN.round().is_nan());
63    }
64
65    #[test]
66    fn round_is_const() {
67        // Just to prove it can be used in const context
68        const _C1: f64 = round(1.7);
69        const _C2: f64 = round(-2.5);
70        const _C3: f64 = round(4503599627370496.7);
71    }
72}