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