1 | /* SPDX-License-Identifier: MIT |
2 | * origin: musl src/math/trunc.c */ |
3 | |
4 | use crate::support::{Float, FpResult, Int, IntTy, MinInt, Status}; |
5 | |
6 | #[inline ] |
7 | pub fn trunc<F: Float>(x: F) -> F { |
8 | trunc_status(x).val |
9 | } |
10 | |
11 | #[inline ] |
12 | pub fn trunc_status<F: Float>(x: F) -> FpResult<F> { |
13 | let mut xi: F::Int = x.to_bits(); |
14 | let e: i32 = x.exp_unbiased(); |
15 | |
16 | // C1: The represented value has no fractional part, so no truncation is needed |
17 | if e >= F::SIG_BITS as i32 { |
18 | return FpResult::ok(x); |
19 | } |
20 | |
21 | let mask = if e < 0 { |
22 | // C2: If the exponent is negative, the result will be zero so we mask out everything |
23 | // except the sign. |
24 | F::SIGN_MASK |
25 | } else { |
26 | // C3: Otherwise, we mask out the last `e` bits of the significand. |
27 | !(F::SIG_MASK >> e.unsigned()) |
28 | }; |
29 | |
30 | // C4: If the to-be-masked-out portion is already zero, we have an exact result |
31 | if (xi & !mask) == IntTy::<F>::ZERO { |
32 | return FpResult::ok(x); |
33 | } |
34 | |
35 | // C5: Otherwise the result is inexact and we will truncate. Raise `FE_INEXACT`, mask the |
36 | // result, and return. |
37 | |
38 | let status = if xi & F::SIG_MASK == F::Int::ZERO { |
39 | Status::OK |
40 | } else { |
41 | Status::INEXACT |
42 | }; |
43 | xi &= mask; |
44 | FpResult::new(F::from_bits(xi), status) |
45 | } |
46 | |
47 | #[cfg (test)] |
48 | mod tests { |
49 | use super::*; |
50 | use crate::support::Hexf; |
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 } = trunc_status(x); |
64 | assert_biteq!(val, x, "{}" , 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 } = trunc_status(x); |
70 | assert_biteq!(val, res, "{}" , Hexf(x)); |
71 | assert_eq!(status, res_stat, "{}" , Hexf(x)); |
72 | } |
73 | } |
74 | |
75 | /* Skipping f16 / f128 "sanity_check"s and spec cases due to rejected literal lexing at MSRV */ |
76 | |
77 | #[test ] |
78 | #[cfg (f16_enabled)] |
79 | fn spec_tests_f16() { |
80 | let cases = []; |
81 | spec_test::<f16>(&cases); |
82 | } |
83 | |
84 | #[test ] |
85 | fn sanity_check_f32() { |
86 | assert_eq!(trunc(0.5f32), 0.0); |
87 | assert_eq!(trunc(1.1f32), 1.0); |
88 | assert_eq!(trunc(2.9f32), 2.0); |
89 | } |
90 | |
91 | #[test ] |
92 | fn spec_tests_f32() { |
93 | let cases = [ |
94 | (0.1, 0.0, Status::INEXACT), |
95 | (-0.1, -0.0, Status::INEXACT), |
96 | (0.9, 0.0, Status::INEXACT), |
97 | (-0.9, -0.0, Status::INEXACT), |
98 | (1.1, 1.0, Status::INEXACT), |
99 | (-1.1, -1.0, Status::INEXACT), |
100 | (1.9, 1.0, Status::INEXACT), |
101 | (-1.9, -1.0, Status::INEXACT), |
102 | ]; |
103 | spec_test::<f32>(&cases); |
104 | |
105 | assert_biteq!(trunc(1.1f32), 1.0); |
106 | assert_biteq!(trunc(1.1f64), 1.0); |
107 | |
108 | // C1 |
109 | assert_biteq!(trunc(hf32!("0x1p23" )), hf32!("0x1p23" )); |
110 | assert_biteq!(trunc(hf64!("0x1p52" )), hf64!("0x1p52" )); |
111 | assert_biteq!(trunc(hf32!("-0x1p23" )), hf32!("-0x1p23" )); |
112 | assert_biteq!(trunc(hf64!("-0x1p52" )), hf64!("-0x1p52" )); |
113 | |
114 | // C2 |
115 | assert_biteq!(trunc(hf32!("0x1p-1" )), 0.0); |
116 | assert_biteq!(trunc(hf64!("0x1p-1" )), 0.0); |
117 | assert_biteq!(trunc(hf32!("-0x1p-1" )), -0.0); |
118 | assert_biteq!(trunc(hf64!("-0x1p-1" )), -0.0); |
119 | } |
120 | |
121 | #[test ] |
122 | fn sanity_check_f64() { |
123 | assert_eq!(trunc(1.1f64), 1.0); |
124 | assert_eq!(trunc(2.9f64), 2.0); |
125 | } |
126 | |
127 | #[test ] |
128 | fn spec_tests_f64() { |
129 | let cases = [ |
130 | (0.1, 0.0, Status::INEXACT), |
131 | (-0.1, -0.0, Status::INEXACT), |
132 | (0.9, 0.0, Status::INEXACT), |
133 | (-0.9, -0.0, Status::INEXACT), |
134 | (1.1, 1.0, Status::INEXACT), |
135 | (-1.1, -1.0, Status::INEXACT), |
136 | (1.9, 1.0, Status::INEXACT), |
137 | (-1.9, -1.0, Status::INEXACT), |
138 | ]; |
139 | spec_test::<f64>(&cases); |
140 | } |
141 | |
142 | #[test ] |
143 | #[cfg (f128_enabled)] |
144 | fn spec_tests_f128() { |
145 | let cases = []; |
146 | spec_test::<f128>(&cases); |
147 | } |
148 | } |
149 | |