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