1 | /* SPDX-License-Identifier: MIT */ |
2 | /* origin: musl src/math/rint.c */ |
3 | |
4 | use super::super::Float; |
5 | use super::super::support::{FpResult, Round}; |
6 | |
7 | /// IEEE 754-2019 `roundToIntegralExact`, which respects rounding mode and raises inexact if |
8 | /// applicable. |
9 | pub 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)] |
44 | mod 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 | |