1 //! Tests of `num_traits::cast`.
2 
3 #![cfg_attr(not(feature = "std"), no_std)]
4 
5 use num_traits::cast::*;
6 use num_traits::Bounded;
7 
8 use core::{f32, f64};
9 use core::{i128, i16, i32, i64, i8, isize};
10 use core::{u128, u16, u32, u64, u8, usize};
11 
12 use core::fmt::Debug;
13 use core::mem;
14 use core::num::Wrapping;
15 
16 #[test]
to_primitive_float()17 fn to_primitive_float() {
18     let f32_toolarge = 1e39f64;
19     assert_eq!(f32_toolarge.to_f32(), Some(f32::INFINITY));
20     assert_eq!((-f32_toolarge).to_f32(), Some(f32::NEG_INFINITY));
21     assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX));
22     assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX));
23     assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY));
24     assert_eq!((f64::NEG_INFINITY).to_f32(), Some(f32::NEG_INFINITY));
25     assert!((f64::NAN).to_f32().map_or(false, |f| f.is_nan()));
26 }
27 
28 #[test]
wrapping_to_primitive()29 fn wrapping_to_primitive() {
30     macro_rules! test_wrapping_to_primitive {
31         ($($t:ty)+) => {
32             $({
33                 let i: $t = 0;
34                 let w = Wrapping(i);
35                 assert_eq!(i.to_u8(),    w.to_u8());
36                 assert_eq!(i.to_u16(),   w.to_u16());
37                 assert_eq!(i.to_u32(),   w.to_u32());
38                 assert_eq!(i.to_u64(),   w.to_u64());
39                 assert_eq!(i.to_usize(), w.to_usize());
40                 assert_eq!(i.to_i8(),    w.to_i8());
41                 assert_eq!(i.to_i16(),   w.to_i16());
42                 assert_eq!(i.to_i32(),   w.to_i32());
43                 assert_eq!(i.to_i64(),   w.to_i64());
44                 assert_eq!(i.to_isize(), w.to_isize());
45                 assert_eq!(i.to_f32(),   w.to_f32());
46                 assert_eq!(i.to_f64(),   w.to_f64());
47             })+
48         };
49     }
50 
51     test_wrapping_to_primitive!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
52 }
53 
54 #[test]
wrapping_is_toprimitive()55 fn wrapping_is_toprimitive() {
56     fn require_toprimitive<T: ToPrimitive>(_: &T) {}
57     require_toprimitive(&Wrapping(42));
58 }
59 
60 #[test]
wrapping_is_fromprimitive()61 fn wrapping_is_fromprimitive() {
62     fn require_fromprimitive<T: FromPrimitive>(_: &T) {}
63     require_fromprimitive(&Wrapping(42));
64 }
65 
66 #[test]
wrapping_is_numcast()67 fn wrapping_is_numcast() {
68     fn require_numcast<T: NumCast>(_: &T) {}
69     require_numcast(&Wrapping(42));
70 }
71 
72 #[test]
as_primitive()73 fn as_primitive() {
74     let x: f32 = (1.625f64).as_();
75     assert_eq!(x, 1.625f32);
76 
77     let x: f32 = (3.14159265358979323846f64).as_();
78     assert_eq!(x, 3.1415927f32);
79 
80     let x: u8 = (768i16).as_();
81     assert_eq!(x, 0);
82 }
83 
84 #[test]
float_to_integer_checks_overflow()85 fn float_to_integer_checks_overflow() {
86     // This will overflow an i32
87     let source: f64 = 1.0e+123f64;
88 
89     // Expect the overflow to be caught
90     assert_eq!(cast::<f64, i32>(source), None);
91 }
92 
93 #[test]
cast_to_int_checks_overflow()94 fn cast_to_int_checks_overflow() {
95     let big_f: f64 = 1.0e123;
96     let normal_f: f64 = 1.0;
97     let small_f: f64 = -1.0e123;
98     assert_eq!(None, cast::<f64, isize>(big_f));
99     assert_eq!(None, cast::<f64, i8>(big_f));
100     assert_eq!(None, cast::<f64, i16>(big_f));
101     assert_eq!(None, cast::<f64, i32>(big_f));
102     assert_eq!(None, cast::<f64, i64>(big_f));
103 
104     assert_eq!(Some(normal_f as isize), cast::<f64, isize>(normal_f));
105     assert_eq!(Some(normal_f as i8), cast::<f64, i8>(normal_f));
106     assert_eq!(Some(normal_f as i16), cast::<f64, i16>(normal_f));
107     assert_eq!(Some(normal_f as i32), cast::<f64, i32>(normal_f));
108     assert_eq!(Some(normal_f as i64), cast::<f64, i64>(normal_f));
109 
110     assert_eq!(None, cast::<f64, isize>(small_f));
111     assert_eq!(None, cast::<f64, i8>(small_f));
112     assert_eq!(None, cast::<f64, i16>(small_f));
113     assert_eq!(None, cast::<f64, i32>(small_f));
114     assert_eq!(None, cast::<f64, i64>(small_f));
115 }
116 
117 #[test]
cast_to_unsigned_int_checks_overflow()118 fn cast_to_unsigned_int_checks_overflow() {
119     let big_f: f64 = 1.0e123;
120     let normal_f: f64 = 1.0;
121     let small_f: f64 = -1.0e123;
122     assert_eq!(None, cast::<f64, usize>(big_f));
123     assert_eq!(None, cast::<f64, u8>(big_f));
124     assert_eq!(None, cast::<f64, u16>(big_f));
125     assert_eq!(None, cast::<f64, u32>(big_f));
126     assert_eq!(None, cast::<f64, u64>(big_f));
127 
128     assert_eq!(Some(normal_f as usize), cast::<f64, usize>(normal_f));
129     assert_eq!(Some(normal_f as u8), cast::<f64, u8>(normal_f));
130     assert_eq!(Some(normal_f as u16), cast::<f64, u16>(normal_f));
131     assert_eq!(Some(normal_f as u32), cast::<f64, u32>(normal_f));
132     assert_eq!(Some(normal_f as u64), cast::<f64, u64>(normal_f));
133 
134     assert_eq!(None, cast::<f64, usize>(small_f));
135     assert_eq!(None, cast::<f64, u8>(small_f));
136     assert_eq!(None, cast::<f64, u16>(small_f));
137     assert_eq!(None, cast::<f64, u32>(small_f));
138     assert_eq!(None, cast::<f64, u64>(small_f));
139 }
140 
141 #[test]
cast_to_i128_checks_overflow()142 fn cast_to_i128_checks_overflow() {
143     let big_f: f64 = 1.0e123;
144     let normal_f: f64 = 1.0;
145     let small_f: f64 = -1.0e123;
146     assert_eq!(None, cast::<f64, i128>(big_f));
147     assert_eq!(None, cast::<f64, u128>(big_f));
148 
149     assert_eq!(Some(normal_f as i128), cast::<f64, i128>(normal_f));
150     assert_eq!(Some(normal_f as u128), cast::<f64, u128>(normal_f));
151 
152     assert_eq!(None, cast::<f64, i128>(small_f));
153     assert_eq!(None, cast::<f64, u128>(small_f));
154 }
155 
156 #[cfg(feature = "std")]
dbg(args: ::core::fmt::Arguments<'_>)157 fn dbg(args: ::core::fmt::Arguments<'_>) {
158     println!("{}", args);
159 }
160 
161 #[cfg(not(feature = "std"))]
dbg(_: ::core::fmt::Arguments)162 fn dbg(_: ::core::fmt::Arguments) {}
163 
164 // Rust 1.8 doesn't handle cfg on macros correctly
165 macro_rules! dbg { ($($tok:tt)*) => { dbg(format_args!($($tok)*)) } }
166 
167 macro_rules! float_test_edge {
168     ($f:ident -> $($t:ident)+) => { $({
169         dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
170 
171         let small = if $t::MIN == 0 || mem::size_of::<$t>() < mem::size_of::<$f>() {
172             $t::MIN as $f - 1.0
173         } else {
174             ($t::MIN as $f).raw_inc().floor()
175         };
176         let fmin = small.raw_dec();
177         dbg!("  testing min {}\n\tvs. {:.0}\n\tand {:.0}", $t::MIN, fmin, small);
178         assert_eq!(Some($t::MIN), cast::<$f, $t>($t::MIN as $f));
179         assert_eq!(Some($t::MIN), cast::<$f, $t>(fmin));
180         assert_eq!(None, cast::<$f, $t>(small));
181 
182         let (max, large) = if mem::size_of::<$t>() < mem::size_of::<$f>() {
183             ($t::MAX, $t::MAX as $f + 1.0)
184         } else {
185             let large = $t::MAX as $f; // rounds up!
186             let max = large.raw_dec() as $t; // the next smallest possible
187             assert_eq!(max.count_ones(), $f::MANTISSA_DIGITS);
188             (max, large)
189         };
190         let fmax = large.raw_dec();
191         dbg!("  testing max {}\n\tvs. {:.0}\n\tand {:.0}", max, fmax, large);
192         assert_eq!(Some(max), cast::<$f, $t>(max as $f));
193         assert_eq!(Some(max), cast::<$f, $t>(fmax));
194         assert_eq!(None, cast::<$f, $t>(large));
195 
196         dbg!("  testing non-finite values");
197         assert_eq!(None, cast::<$f, $t>($f::NAN));
198         assert_eq!(None, cast::<$f, $t>($f::INFINITY));
199         assert_eq!(None, cast::<$f, $t>($f::NEG_INFINITY));
200     })+}
201 }
202 
203 trait RawOffset: Sized {
raw_inc(self) -> Self204     fn raw_inc(self) -> Self;
raw_dec(self) -> Self205     fn raw_dec(self) -> Self;
206 }
207 
208 impl RawOffset for f32 {
raw_inc(self) -> Self209     fn raw_inc(self) -> Self {
210         Self::from_bits(self.to_bits() + 1)
211     }
212 
raw_dec(self) -> Self213     fn raw_dec(self) -> Self {
214         Self::from_bits(self.to_bits() - 1)
215     }
216 }
217 
218 impl RawOffset for f64 {
raw_inc(self) -> Self219     fn raw_inc(self) -> Self {
220         Self::from_bits(self.to_bits() + 1)
221     }
222 
raw_dec(self) -> Self223     fn raw_dec(self) -> Self {
224         Self::from_bits(self.to_bits() - 1)
225     }
226 }
227 
228 #[test]
cast_float_to_int_edge_cases()229 fn cast_float_to_int_edge_cases() {
230     float_test_edge!(f32 -> isize i8 i16 i32 i64);
231     float_test_edge!(f32 -> usize u8 u16 u32 u64);
232     float_test_edge!(f64 -> isize i8 i16 i32 i64);
233     float_test_edge!(f64 -> usize u8 u16 u32 u64);
234 }
235 
236 #[test]
cast_float_to_i128_edge_cases()237 fn cast_float_to_i128_edge_cases() {
238     float_test_edge!(f32 -> i128 u128);
239     float_test_edge!(f64 -> i128 u128);
240 }
241 
242 macro_rules! int_test_edge {
243     ($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({
244         #[allow(arithmetic_overflow)] // https://github.com/rust-lang/rust/issues/109731
245         fn test_edge() {
246             dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
247 
248             match ($f::MIN as $BigS).cmp(&($t::MIN as $BigS)) {
249                 Greater => {
250                     assert_eq!(Some($f::MIN as $t), cast::<$f, $t>($f::MIN));
251                 }
252                 Equal => {
253                     assert_eq!(Some($t::MIN), cast::<$f, $t>($f::MIN));
254                 }
255                 Less => {
256                     let min = $t::MIN as $f;
257                     assert_eq!(Some($t::MIN), cast::<$f, $t>(min));
258                     assert_eq!(None, cast::<$f, $t>(min - 1));
259                 }
260             }
261 
262             match ($f::MAX as $BigU).cmp(&($t::MAX as $BigU)) {
263                 Greater => {
264                     let max = $t::MAX as $f;
265                     assert_eq!(Some($t::MAX), cast::<$f, $t>(max));
266                     assert_eq!(None, cast::<$f, $t>(max + 1));
267                 }
268                 Equal => {
269                     assert_eq!(Some($t::MAX), cast::<$f, $t>($f::MAX));
270                 }
271                 Less => {
272                     assert_eq!(Some($f::MAX as $t), cast::<$f, $t>($f::MAX));
273                 }
274             }
275         }
276         test_edge();
277     })+}
278 }
279 
280 #[test]
cast_int_to_int_edge_cases()281 fn cast_int_to_int_edge_cases() {
282     use core::cmp::Ordering::*;
283 
284     macro_rules! test_edge {
285         ($( $from:ident )+) => { $({
286             int_test_edge!($from -> { isize i8 i16 i32 i64 } with i64 u64);
287             int_test_edge!($from -> { usize u8 u16 u32 u64 } with i64 u64);
288         })+}
289     }
290 
291     test_edge!(isize i8 i16 i32 i64);
292     test_edge!(usize u8 u16 u32 u64);
293 }
294 
295 #[test]
cast_int_to_128_edge_cases()296 fn cast_int_to_128_edge_cases() {
297     use core::cmp::Ordering::*;
298 
299     macro_rules! test_edge {
300         ($( $t:ident )+) => {
301             $(
302                 int_test_edge!($t -> { i128 u128 } with i128 u128);
303             )+
304             int_test_edge!(i128 -> { $( $t )+ } with i128 u128);
305             int_test_edge!(u128 -> { $( $t )+ } with i128 u128);
306         }
307     }
308 
309     test_edge!(isize i8 i16 i32 i64 i128);
310     test_edge!(usize u8 u16 u32 u64 u128);
311 }
312 
313 #[test]
newtype_from_primitive()314 fn newtype_from_primitive() {
315     #[derive(PartialEq, Debug)]
316     struct New<T>(T);
317 
318     // minimal impl
319     impl<T: FromPrimitive> FromPrimitive for New<T> {
320         fn from_i64(n: i64) -> Option<Self> {
321             T::from_i64(n).map(New)
322         }
323 
324         fn from_u64(n: u64) -> Option<Self> {
325             T::from_u64(n).map(New)
326         }
327     }
328 
329     macro_rules! assert_eq_from {
330         ($( $from:ident )+) => {$(
331             assert_eq!(T::$from(Bounded::min_value()).map(New),
332                        New::<T>::$from(Bounded::min_value()));
333             assert_eq!(T::$from(Bounded::max_value()).map(New),
334                        New::<T>::$from(Bounded::max_value()));
335         )+}
336     }
337 
338     fn check<T: PartialEq + Debug + FromPrimitive>() {
339         assert_eq_from!(from_i8 from_i16 from_i32 from_i64 from_isize);
340         assert_eq_from!(from_u8 from_u16 from_u32 from_u64 from_usize);
341         assert_eq_from!(from_f32 from_f64);
342     }
343 
344     macro_rules! check {
345         ($( $ty:ty )+) => {$( check::<$ty>(); )+}
346     }
347     check!(i8 i16 i32 i64 isize);
348     check!(u8 u16 u32 u64 usize);
349 }
350 
351 #[test]
newtype_to_primitive()352 fn newtype_to_primitive() {
353     #[derive(PartialEq, Debug)]
354     struct New<T>(T);
355 
356     // minimal impl
357     impl<T: ToPrimitive> ToPrimitive for New<T> {
358         fn to_i64(&self) -> Option<i64> {
359             self.0.to_i64()
360         }
361 
362         fn to_u64(&self) -> Option<u64> {
363             self.0.to_u64()
364         }
365     }
366 
367     macro_rules! assert_eq_to {
368         ($( $to:ident )+) => {$(
369             assert_eq!(T::$to(&Bounded::min_value()),
370                        New::<T>::$to(&New(Bounded::min_value())));
371             assert_eq!(T::$to(&Bounded::max_value()),
372                        New::<T>::$to(&New(Bounded::max_value())));
373         )+}
374     }
375 
376     fn check<T: PartialEq + Debug + Bounded + ToPrimitive>() {
377         assert_eq_to!(to_i8 to_i16 to_i32 to_i64 to_isize);
378         assert_eq_to!(to_u8 to_u16 to_u32 to_u64 to_usize);
379         assert_eq_to!(to_f32 to_f64);
380     }
381 
382     macro_rules! check {
383         ($( $ty:ty )+) => {$( check::<$ty>(); )+}
384     }
385     check!(i8 i16 i32 i64 isize);
386     check!(u8 u16 u32 u64 usize);
387 }
388