1use super::*;
9
10#[allow(clippy::manual_range_contains)]
11impl ConvApprox<f64> for f32 {
12 fn try_conv_approx(x: f64) -> Result<f32> {
13 use core::num::FpCategory;
14
15 let sign_bits = (x.to_bits() >> 32) as u32 & 0x8000_0000;
16 let with_sign = |x: f32| -> f32 {
17 f32::from_bits(sign_bits | x.to_bits())
19 };
20
21 match x.classify() {
22 FpCategory::Nan => Err(Error::Range),
23 FpCategory::Infinite => Ok(with_sign(f32::INFINITY)),
24 FpCategory::Zero | FpCategory::Subnormal => Ok(with_sign(0f32)),
25 FpCategory::Normal => {
26 let exp = (x.to_bits() & 0x7FF0_0000_0000_0000) >> 52;
29 if exp >= 1023 - 126 && exp <= 1023 + 127 {
30 let exp = ((exp + 127) - 1023) as u32;
31 let frac = ((x.to_bits() & 0x000F_FFFF_FFFF_FFFF) >> (52 - 23)) as u32;
32 let bits = sign_bits | (exp << 23) | frac;
33 Ok(f32::from_bits(bits))
34 } else {
35 Err(Error::Range)
36 }
37 }
38 }
39 }
40
41 fn conv_approx(x: f64) -> f32 {
42 if cfg!(any(debug_assertions, feature = "assert_float")) {
43 Self::try_conv_approx(x).unwrap_or_else(|_| {
44 panic!("cast x: f64 to f32 (approx): range error for x = {}", x)
45 })
46 } else {
47 x as f32
48 }
49 }
50}
51
52#[cfg(all(not(feature = "std"), feature = "libm"))]
53trait FloatRound {
54 fn round(self) -> Self;
55 fn floor(self) -> Self;
56 fn ceil(self) -> Self;
57}
58#[cfg(all(not(feature = "std"), feature = "libm"))]
59impl FloatRound for f32 {
60 fn round(self) -> Self {
61 libm::roundf(self)
62 }
63 fn floor(self) -> Self {
64 libm::floorf(self)
65 }
66 fn ceil(self) -> Self {
67 libm::ceilf(self)
68 }
69}
70#[cfg(all(not(feature = "std"), feature = "libm"))]
71impl FloatRound for f64 {
72 fn round(self) -> Self {
73 libm::round(self)
74 }
75 fn floor(self) -> Self {
76 libm::floor(self)
77 }
78 fn ceil(self) -> Self {
79 libm::ceil(self)
80 }
81}
82
83#[cfg(any(feature = "std", feature = "libm"))]
84macro_rules! impl_float {
85 ($x:ty: $y:tt) => {
86 impl ConvFloat<$x> for $y {
87 #[inline]
88 fn conv_trunc(x: $x) -> $y {
89 if cfg!(any(debug_assertions, feature = "assert_float")) {
90 Self::try_conv_trunc(x).unwrap_or_else(|_| {
91 panic!(
92 "cast x: {} to {} (trunc): range error for x = {}",
93 stringify!($x), stringify!($y), x
94 )
95 })
96 } else {
97 x as $y
98 }
99 }
100 #[inline]
101 fn conv_nearest(x: $x) -> $y {
102 if cfg!(any(debug_assertions, feature = "assert_float")) {
103 Self::try_conv_nearest(x).unwrap_or_else(|_| {
104 panic!(
105 "cast x: {} to {} (nearest): range error for x = {}",
106 stringify!($x), stringify!($y), x
107 )
108 })
109 } else {
110 x.round() as $y
111 }
112 }
113 #[inline]
114 fn conv_floor(x: $x) -> $y {
115 if cfg!(any(debug_assertions, feature = "assert_float")) {
116 Self::try_conv_floor(x).unwrap_or_else(|_| {
117 panic!(
118 "cast x: {} to {} (floor): range error for x = {}",
119 stringify!($x), stringify!($y), x
120 )
121 })
122 } else {
123 x.floor() as $y
124 }
125 }
126 #[inline]
127 fn conv_ceil(x: $x) -> $y {
128 if cfg!(any(debug_assertions, feature = "assert_float")) {
129 Self::try_conv_ceil(x).unwrap_or_else(|_| {
130 panic!(
131 "cast x: {} to {} (ceil): range error for x = {}",
132 stringify!($x), stringify!($y), x
133 )
134 })
135 } else {
136 x.ceil() as $y
137 }
138 }
139
140 #[inline]
141 fn try_conv_trunc(x: $x) -> Result<Self> {
142 const LBOUND: $x = $y::MIN as $x - 1.0;
144 const UBOUND: $x = $y::MAX as $x + 1.0;
145 if x > LBOUND && x < UBOUND {
146 Ok(x as $y)
147 } else {
148 Err(Error::Range)
149 }
150 }
151 #[inline]
152 fn try_conv_nearest(x: $x) -> Result<Self> {
153 const LBOUND: $x = $y::MIN as $x;
155 const UBOUND: $x = $y::MAX as $x + 1.0;
156 let x = x.round();
157 if (LBOUND..UBOUND).contains(&x) {
158 Ok(x as $y)
159 } else {
160 Err(Error::Range)
161 }
162 }
163 #[inline]
164 fn try_conv_floor(x: $x) -> Result<Self> {
165 const LBOUND: $x = $y::MIN as $x;
167 const UBOUND: $x = $y::MAX as $x + 1.0;
168 let x = x.floor();
169 if (LBOUND..UBOUND).contains(&x) {
170 Ok(x as $y)
171 } else {
172 Err(Error::Range)
173 }
174 }
175 #[inline]
176 fn try_conv_ceil(x: $x) -> Result<Self> {
177 const LBOUND: $x = $y::MIN as $x;
179 const UBOUND: $x = $y::MAX as $x + 1.0;
180 let x = x.ceil();
181 if (LBOUND..UBOUND).contains(&x) {
182 Ok(x as $y)
183 } else {
184 Err(Error::Range)
185 }
186 }
187 }
188
189 impl ConvApprox<$x> for $y {
190 #[inline]
191 fn try_conv_approx(x: $x) -> Result<Self> {
192 ConvFloat::<$x>::try_conv_trunc(x)
193 }
194 #[inline]
195 fn conv_approx(x: $x) -> Self {
196 ConvFloat::<$x>::conv_trunc(x)
197 }
198 }
199 };
200 ($x:ty: $y:tt, $($yy:tt),+) => {
201 impl_float!($x: $y);
202 impl_float!($x: $($yy),+);
203 };
204}
205
206#[cfg(any(feature = "std", feature = "libm"))]
208impl_float!(f32: i8, i16, i32, i64, i128, isize);
209#[cfg(any(feature = "std", feature = "libm"))]
210impl_float!(f32: u8, u16, u32, u64, usize);
211#[cfg(any(feature = "std", feature = "libm"))]
212impl_float!(f64: i8, i16, i32, i64, i128, isize);
213#[cfg(any(feature = "std", feature = "libm"))]
214impl_float!(f64: u8, u16, u32, u64, u128, usize);
215
216#[cfg(any(feature = "std", feature = "libm"))]
217impl ConvFloat<f32> for u128 {
218 #[inline]
219 fn conv_trunc(x: f32) -> u128 {
220 if cfg!(any(debug_assertions, feature = "assert_float")) {
221 Self::try_conv_trunc(x).unwrap_or_else(|_| {
222 panic!(
223 "cast x: f32 to u128 (trunc/floor): range error for x = {}",
224 x
225 )
226 })
227 } else {
228 x as u128
229 }
230 }
231 #[inline]
232 fn conv_nearest(x: f32) -> u128 {
233 if cfg!(any(debug_assertions, feature = "assert_float")) {
234 Self::try_conv_nearest(x).unwrap_or_else(|_| {
235 panic!("cast x: f32 to u128 (nearest): range error for x = {}", x)
236 })
237 } else {
238 x.round() as u128
239 }
240 }
241 #[inline]
242 fn conv_floor(x: f32) -> u128 {
243 ConvFloat::conv_trunc(x)
244 }
245 #[inline]
246 fn conv_ceil(x: f32) -> u128 {
247 if cfg!(any(debug_assertions, feature = "assert_float")) {
248 Self::try_conv_ceil(x)
249 .unwrap_or_else(|_| panic!("cast x: f32 to u128 (ceil): range error for x = {}", x))
250 } else {
251 x.ceil() as u128
252 }
253 }
254
255 #[inline]
256 fn try_conv_trunc(x: f32) -> Result<Self> {
257 if x >= 0.0 && x.is_finite() {
259 Ok(x as u128)
260 } else {
261 Err(Error::Range)
262 }
263 }
264 #[inline]
265 fn try_conv_nearest(x: f32) -> Result<Self> {
266 let x = x.round();
267 if x >= 0.0 && x.is_finite() {
268 Ok(x as u128)
269 } else {
270 Err(Error::Range)
271 }
272 }
273 #[inline]
274 fn try_conv_floor(x: f32) -> Result<Self> {
275 Self::try_conv_trunc(x)
276 }
277 #[inline]
278 fn try_conv_ceil(x: f32) -> Result<Self> {
279 let x = x.ceil();
280 if x >= 0.0 && x.is_finite() {
281 Ok(x as u128)
282 } else {
283 Err(Error::Range)
284 }
285 }
286}
287
288#[cfg(any(feature = "std", feature = "libm"))]
289impl ConvApprox<f32> for u128 {
290 #[inline]
291 fn try_conv_approx(x: f32) -> Result<Self> {
292 ConvFloat::<f32>::try_conv_trunc(x)
293 }
294 #[inline]
295 fn conv_approx(x: f32) -> Self {
296 ConvFloat::<f32>::conv_trunc(x)
297 }
298}