1/* SPDX-License-Identifier: MIT
2 * origin: musl src/math/trunc.c */
3
4use super::super::support::{FpResult, Status};
5use super::super::{Float, Int, IntTy, MinInt};
6
7#[inline]
8pub fn trunc<F: Float>(x: F) -> F {
9 trunc_status(x).val
10}
11
12#[inline]
13pub 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)]
49mod 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