exact_conv/
lib.rs

1#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2pub enum ExactError {
3    Inexact,
4    Overflow,
5}
6
7pub trait ExactFrom<T>: Sized {
8    type Error;
9    fn exact_from(value: T) -> Result<Self, Self::Error>;
10}
11
12pub trait ExactInto<T> {
13    type Error;
14    fn exact_into(self) -> Result<T, Self::Error>;
15}
16
17impl<T, U> ExactInto<U> for T
18where
19    U: ExactFrom<T>,
20{
21    type Error = <U as ExactFrom<T>>::Error;
22    fn exact_into(self) -> Result<U, Self::Error> {
23        <U as ExactFrom<T>>::exact_from(self)
24    }
25}
26
27macro_rules! float_to_int {
28    ($($F:ty),* => $n:literal: $U:ty, $S:ty) => {$(
29        impl ExactFrom<$F> for $U {
30            type Error = ExactError;
31            fn exact_from(value: $F) -> Result<$U, Self::Error> {
32                let min: $F = 0 as $F;
33                let max: $F = (2 as $F).powi($n);
34                if min <= value && value < max {
35                    if value.trunc() == value {
36                        Ok(value as $U)
37                    } else {
38                        Err(ExactError::Inexact)
39                    }
40                } else {
41                    Err(ExactError::Overflow)
42                }
43            }
44        }
45        impl ExactFrom<$F> for $S {
46            type Error = ExactError;
47            fn exact_from(value: $F) -> Result<$S, Self::Error> {
48                let max: $F = (2 as $F).powi($n - 1);
49                let min: $F = -max;
50                if min <= value && value < max {
51                    if value.trunc() == value {
52                        Ok(value as $S)
53                    } else {
54                        Err(ExactError::Inexact)
55                    }
56                } else {
57                    Err(ExactError::Overflow)
58                }
59            }
60        }
61    )*};
62}
63
64float_to_int!(f32, f64 => 8: u8, i8);
65float_to_int!(f32, f64 => 16: u16, i16);
66float_to_int!(f32, f64 => 32: u32, i32);
67float_to_int!(f32, f64 => 64: u64, i64);
68float_to_int!(f32, f64 => 128: u128, i128);
69
70#[cfg(target_pointer_width = "16")]
71float_to_int!(f32, f64 => 16: usize, isize);
72
73#[cfg(target_pointer_width = "32")]
74float_to_int!(f32, f64 => 32: usize, isize);
75
76#[cfg(target_pointer_width = "64")]
77float_to_int!(f32, f64 => 64: usize, isize);
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn f32_to_unsigned() {
85        assert_eq!(0_f32.exact_into(), Ok(0_u8));
86        assert_eq!(0_f32.exact_into(), Ok(0_u16));
87        assert_eq!(0_f32.exact_into(), Ok(0_u32));
88        assert_eq!(0_f32.exact_into(), Ok(0_u64));
89        assert_eq!(0_f32.exact_into(), Ok(0_u128));
90
91        assert_eq!(255_f32.exact_into(), Ok(u8::MAX));
92        assert_eq!(65535_f32.exact_into(), Ok(u16::MAX));
93        assert_eq!(4294967040_f32.exact_into(), Ok(4294967040_u32));
94        assert_eq!(
95            18446742974197923840_f32.exact_into(),
96            Ok(18446742974197923840_u64)
97        );
98        assert_eq!(
99            340282346638528859811704183484516925440_f32.exact_into(),
100            Ok(340282346638528859811704183484516925440_u128)
101        );
102    }
103
104    #[test]
105    fn f32_to_signed() {
106        assert_eq!((-128_f32).exact_into(), Ok(i8::MIN));
107        assert_eq!((-32768_f32).exact_into(), Ok(i16::MIN));
108        assert_eq!((-2147483648_f32).exact_into(), Ok(-2147483648_i32));
109        assert_eq!(
110            (-9223372036854775808_f32).exact_into(),
111            Ok(-9223372036854775808_i64)
112        );
113        assert_eq!(
114            (-170141183460469231731687303715884105728_f32).exact_into(),
115            Ok(-170141183460469231731687303715884105728_i128)
116        );
117
118        assert_eq!(127_f32.exact_into(), Ok(i8::MAX));
119        assert_eq!(32767_f32.exact_into(), Ok(i16::MAX));
120        assert_eq!(2147483520_f32.exact_into(), Ok(2147483520_i32));
121        assert_eq!(
122            9223371487098961920_f32.exact_into(),
123            Ok(9223371487098961920_i64)
124        );
125        assert_eq!(
126            170141173319264429905852091742258462720_f32.exact_into(),
127            Ok(170141173319264429905852091742258462720_i128)
128        );
129    }
130
131    #[test]
132    fn f64_to_unsigned() {
133        assert_eq!(0_f64.exact_into(), Ok(0_u8));
134        assert_eq!(0_f64.exact_into(), Ok(0_u16));
135        assert_eq!(0_f64.exact_into(), Ok(0_u32));
136        assert_eq!(0_f64.exact_into(), Ok(0_u64));
137        assert_eq!(0_f64.exact_into(), Ok(0_u128));
138
139        assert_eq!(255_f64.exact_into(), Ok(u8::MAX));
140        assert_eq!(65535_f64.exact_into(), Ok(u16::MAX));
141        assert_eq!(4294967295_f64.exact_into(), Ok(u32::MAX));
142        assert_eq!(
143            18446744073709549568_f64.exact_into(),
144            Ok(18446744073709549568_u64)
145        );
146        assert_eq!(
147            340282366920938425684442744474606501888_f64.exact_into(),
148            Ok(340282366920938425684442744474606501888_u128)
149        );
150    }
151
152    #[test]
153    fn f64_to_signed() {
154        assert_eq!((-128_f64).exact_into(), Ok(i8::MIN));
155        assert_eq!((-32768_f64).exact_into(), Ok(i16::MIN));
156        assert_eq!((-2147483648_f64).exact_into(), Ok(i32::MIN));
157        assert_eq!(
158            (-9223372036854775808_f64).exact_into(),
159            Ok(-9223372036854775808_i64)
160        );
161        assert_eq!(
162            (-170141183460469231731687303715884105728_f64).exact_into(),
163            Ok(-170141183460469231731687303715884105728_i128)
164        );
165
166        assert_eq!(127_f64.exact_into(), Ok(i8::MAX));
167        assert_eq!(32767_f64.exact_into(), Ok(i16::MAX));
168        assert_eq!(2147483647_f64.exact_into(), Ok(i32::MAX));
169        assert_eq!(
170            9223372036854774784_f64.exact_into(),
171            Ok(9223372036854774784_i64)
172        );
173        assert_eq!(
174            170141183460469212842221372237303250944_f64.exact_into(),
175            Ok(170141183460469212842221372237303250944_i128)
176        );
177    }
178
179    #[test]
180    fn inexact() {
181        assert_eq!(i8::exact_from(3.14_f32), Err(ExactError::Inexact));
182    }
183
184    #[test]
185    fn f32_to_unsigned_overflow() {
186        assert_eq!(u8::exact_from(-1_f32), Err(ExactError::Overflow));
187        assert_eq!(u16::exact_from(-1_f32), Err(ExactError::Overflow));
188        assert_eq!(u32::exact_from(-1_f32), Err(ExactError::Overflow));
189        assert_eq!(u64::exact_from(-1_f32), Err(ExactError::Overflow));
190        assert_eq!(u128::exact_from(-1_f32), Err(ExactError::Overflow));
191
192        assert_eq!(u8::exact_from((2_f32).powi(8)), Err(ExactError::Overflow));
193        assert_eq!(u16::exact_from((2_f32).powi(16)), Err(ExactError::Overflow));
194        assert_eq!(u32::exact_from((2_f32).powi(32)), Err(ExactError::Overflow));
195        assert_eq!(u64::exact_from((2_f32).powi(64)), Err(ExactError::Overflow));
196        assert_eq!(
197            u128::exact_from((2_f32).powi(128)),
198            Err(ExactError::Overflow)
199        );
200    }
201
202    #[test]
203    fn f32_to_signed_overflow() {
204        assert_eq!(
205            i8::exact_from(-(2_f32).powi(8 - 1) - 1_f32),
206            Err(ExactError::Overflow)
207        );
208        assert_eq!(
209            i16::exact_from(-(2_f32).powi(16 - 1) - 1_f32),
210            Err(ExactError::Overflow)
211        );
212        assert_eq!(i32::exact_from(-2147483904_f32), Err(ExactError::Overflow));
213        assert_eq!(
214            i64::exact_from(-9223373136366403584_f32),
215            Err(ExactError::Overflow)
216        );
217        assert_eq!(
218            i128::exact_from(-170141203742878835383357727663135391744_f32),
219            Err(ExactError::Overflow)
220        );
221
222        assert_eq!(
223            i8::exact_from((2_f32).powi(8 - 1)),
224            Err(ExactError::Overflow)
225        );
226        assert_eq!(
227            i16::exact_from((2_f32).powi(16 - 1)),
228            Err(ExactError::Overflow)
229        );
230        assert_eq!(
231            i32::exact_from((2_f32).powi(32 - 1)),
232            Err(ExactError::Overflow)
233        );
234        assert_eq!(
235            i64::exact_from((2_f32).powi(64 - 1)),
236            Err(ExactError::Overflow)
237        );
238        assert_eq!(
239            i128::exact_from((2_f32).powi(128 - 1)),
240            Err(ExactError::Overflow)
241        );
242    }
243
244    #[test]
245    fn f64_to_unsigned_overflow() {
246        assert_eq!(u8::exact_from(-1_f64), Err(ExactError::Overflow));
247        assert_eq!(u16::exact_from(-1_f64), Err(ExactError::Overflow));
248        assert_eq!(u32::exact_from(-1_f64), Err(ExactError::Overflow));
249        assert_eq!(u64::exact_from(-1_f64), Err(ExactError::Overflow));
250        assert_eq!(u128::exact_from(-1_f64), Err(ExactError::Overflow));
251
252        assert_eq!(u8::exact_from((2_f64).powi(8)), Err(ExactError::Overflow));
253        assert_eq!(u16::exact_from((2_f64).powi(16)), Err(ExactError::Overflow));
254        assert_eq!(u32::exact_from((2_f64).powi(32)), Err(ExactError::Overflow));
255        assert_eq!(u64::exact_from((2_f64).powi(64)), Err(ExactError::Overflow));
256        assert_eq!(
257            u128::exact_from((2_f64).powi(128)),
258            Err(ExactError::Overflow)
259        );
260    }
261
262    #[test]
263    fn f64_to_signed_overflow() {
264        assert_eq!(
265            i8::exact_from(-(2_f64).powi(8 - 1) - 1_f64),
266            Err(ExactError::Overflow)
267        );
268        assert_eq!(
269            i16::exact_from(-(2_f64).powi(16 - 1) - 1_f64),
270            Err(ExactError::Overflow)
271        );
272        assert_eq!(
273            i32::exact_from(-(2_f64).powi(32 - 1) - 1_f64),
274            Err(ExactError::Overflow)
275        );
276        assert_eq!(
277            i64::exact_from(-9223372036854777856_f64),
278            Err(ExactError::Overflow)
279        );
280        assert_eq!(
281            i128::exact_from(-170141183460469269510619166673045815296_f64),
282            Err(ExactError::Overflow)
283        );
284
285        assert_eq!(
286            i8::exact_from((2_f64).powi(8 - 1)),
287            Err(ExactError::Overflow)
288        );
289        assert_eq!(
290            i16::exact_from((2_f64).powi(16 - 1)),
291            Err(ExactError::Overflow)
292        );
293        assert_eq!(
294            i32::exact_from((2_f64).powi(32 - 1)),
295            Err(ExactError::Overflow)
296        );
297        assert_eq!(
298            i64::exact_from((2_f64).powi(64 - 1)),
299            Err(ExactError::Overflow)
300        );
301        assert_eq!(
302            i128::exact_from((2_f64).powi(128 - 1)),
303            Err(ExactError::Overflow)
304        );
305    }
306}
307
308pub trait IEEE754 {
309    const BITS: i16;
310    const EXP_BITS: i16;
311    const SIG_BITS: i16;
312    type Unsigned;
313    const EXP_MASK: Self::Unsigned; // = ((1 << Self::EXP_BITS) - 1) << Self::SIG_BITS;
314    const SIG_MASK: Self::Unsigned; // = (1 << Self::SIG_MASK) - 1;
315    const EXP_BIAS: Self::Unsigned;
316}
317
318impl IEEE754 for f32 {
319    const BITS: i16 = 32;
320    const EXP_BITS: i16 = 8;
321    const SIG_BITS: i16 = 23;
322    type Unsigned = u32;
323    const EXP_MASK: Self::Unsigned = ((1 << Self::EXP_BITS) - 1) << Self::SIG_BITS;
324    const SIG_MASK: Self::Unsigned = (1 << Self::SIG_BITS) - 1;
325    const EXP_BIAS: Self::Unsigned = (1 << (Self::EXP_BITS - 1)) - 1;
326}
327
328impl IEEE754 for f64 {
329    const BITS: i16 = 64;
330    const EXP_BITS: i16 = 11;
331    const SIG_BITS: i16 = 52;
332    type Unsigned = u64;
333    const EXP_MASK: Self::Unsigned = ((1 << Self::EXP_BITS) - 1) << Self::SIG_BITS;
334    const SIG_MASK: Self::Unsigned = (1 << Self::SIG_BITS) - 1;
335    const EXP_BIAS: Self::Unsigned = (1 << (Self::EXP_BITS - 1)) - 1;
336}