compiler_builtins/math/libm_math/generic/
rint.rs

1/* SPDX-License-Identifier: MIT */
2/* origin: musl src/math/rint.c */
3
4use crate::support::{Float, FpResult, Round};
5
6/// IEEE 754-2019 `roundToIntegralExact`, which respects rounding mode and raises inexact if
7/// applicable.
8#[inline]
9pub fn rint_round<F: Float>(x: F, _round: Round) -> FpResult<F> {
10    let toint = F::ONE / F::EPSILON;
11    let e = x.ex();
12    let positive = x.is_sign_positive();
13
14    // On i386 `force_eval!` must be used to force rounding via storage to memory. Otherwise,
15    // the excess precission from x87 would cause an incorrect final result.
16    let force = |x| {
17        if cfg!(x86_no_sse) && (F::BITS == 32 || F::BITS == 64) {
18            force_eval!(x)
19        } else {
20            x
21        }
22    };
23
24    let res = if e >= F::EXP_BIAS + F::SIG_BITS {
25        // No fractional part; exact result can be returned.
26        x
27    } else {
28        // Apply a net-zero adjustment that nudges `y` in the direction of the rounding mode. For
29        // Rust this is always nearest, but ideally it would take `round` into account.
30        let y = if positive {
31            force(force(x) + toint) - toint
32        } else {
33            force(force(x) - toint) + toint
34        };
35
36        if y == F::ZERO {
37            // A zero result takes the sign of the input.
38            if positive { F::ZERO } else { F::NEG_ZERO }
39        } else {
40            y
41        }
42    };
43
44    FpResult::ok(res)
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use crate::support::{Hexf, Status};
51
52    fn spec_test<F: Float>(cases: &[(F, F, Status)]) {
53        let roundtrip = [
54            F::ZERO,
55            F::ONE,
56            F::NEG_ONE,
57            F::NEG_ZERO,
58            F::INFINITY,
59            F::NEG_INFINITY,
60        ];
61
62        for x in roundtrip {
63            let FpResult { val, status } = rint_round(x, Round::Nearest);
64            assert_biteq!(val, x, "rint_round({})", Hexf(x));
65            assert_eq!(status, Status::OK, "{}", Hexf(x));
66        }
67
68        for &(x, res, res_stat) in cases {
69            let FpResult { val, status } = rint_round(x, Round::Nearest);
70            assert_biteq!(val, res, "rint_round({})", Hexf(x));
71            assert_eq!(status, res_stat, "{}", Hexf(x));
72        }
73    }
74
75    #[test]
76    #[cfg(f16_enabled)]
77    fn spec_tests_f16() {
78        let cases = [];
79        spec_test::<f16>(&cases);
80    }
81
82    #[test]
83    fn spec_tests_f32() {
84        let cases = [
85            (0.1, 0.0, Status::OK),
86            (-0.1, -0.0, Status::OK),
87            (0.5, 0.0, Status::OK),
88            (-0.5, -0.0, Status::OK),
89            (0.9, 1.0, Status::OK),
90            (-0.9, -1.0, Status::OK),
91            (1.1, 1.0, Status::OK),
92            (-1.1, -1.0, Status::OK),
93            (1.5, 2.0, Status::OK),
94            (-1.5, -2.0, Status::OK),
95            (1.9, 2.0, Status::OK),
96            (-1.9, -2.0, Status::OK),
97            (2.8, 3.0, Status::OK),
98            (-2.8, -3.0, Status::OK),
99        ];
100        spec_test::<f32>(&cases);
101    }
102
103    #[test]
104    fn spec_tests_f64() {
105        let cases = [
106            (0.1, 0.0, Status::OK),
107            (-0.1, -0.0, Status::OK),
108            (0.5, 0.0, Status::OK),
109            (-0.5, -0.0, Status::OK),
110            (0.9, 1.0, Status::OK),
111            (-0.9, -1.0, Status::OK),
112            (1.1, 1.0, Status::OK),
113            (-1.1, -1.0, Status::OK),
114            (1.5, 2.0, Status::OK),
115            (-1.5, -2.0, Status::OK),
116            (1.9, 2.0, Status::OK),
117            (-1.9, -2.0, Status::OK),
118            (2.8, 3.0, Status::OK),
119            (-2.8, -3.0, Status::OK),
120        ];
121        spec_test::<f64>(&cases);
122    }
123
124    #[test]
125    #[cfg(f128_enabled)]
126    fn spec_tests_f128() {
127        let cases = [];
128        spec_test::<f128>(&cases);
129    }
130}