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