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