1 //! Tests of `num_traits::cast`.
2 
3 #![no_std]
4 
5 #[cfg(feature = "std")]
6 #[macro_use]
7 extern crate std;
8 
9 extern crate num_traits;
10 
11 use num_traits::cast::*;
12 
13 use core::{i8, i16, i32, i64, isize};
14 use core::{u8, u16, u32, u64, usize};
15 use core::{f32, f64};
16 #[cfg(has_i128)]
17 use core::{i128, u128};
18 
19 use core::mem;
20 use core::num::Wrapping;
21 
22 #[test]
to_primitive_float()23 fn to_primitive_float() {
24     let f32_toolarge = 1e39f64;
25     assert_eq!(f32_toolarge.to_f32(), None);
26     assert_eq!((f32::MAX as f64).to_f32(), Some(f32::MAX));
27     assert_eq!((-f32::MAX as f64).to_f32(), Some(-f32::MAX));
28     assert_eq!(f64::INFINITY.to_f32(), Some(f32::INFINITY));
29     assert_eq!((f64::NEG_INFINITY).to_f32(), Some(f32::NEG_INFINITY));
30     assert!((f64::NAN).to_f32().map_or(false, |f| f.is_nan()));
31 }
32 
33 #[test]
wrapping_to_primitive()34 fn wrapping_to_primitive() {
35     macro_rules! test_wrapping_to_primitive {
36         ($($t:ty)+) => {
37             $({
38                 let i: $t = 0;
39                 let w = Wrapping(i);
40                 assert_eq!(i.to_u8(),    w.to_u8());
41                 assert_eq!(i.to_u16(),   w.to_u16());
42                 assert_eq!(i.to_u32(),   w.to_u32());
43                 assert_eq!(i.to_u64(),   w.to_u64());
44                 assert_eq!(i.to_usize(), w.to_usize());
45                 assert_eq!(i.to_i8(),    w.to_i8());
46                 assert_eq!(i.to_i16(),   w.to_i16());
47                 assert_eq!(i.to_i32(),   w.to_i32());
48                 assert_eq!(i.to_i64(),   w.to_i64());
49                 assert_eq!(i.to_isize(), w.to_isize());
50                 assert_eq!(i.to_f32(),   w.to_f32());
51                 assert_eq!(i.to_f64(),   w.to_f64());
52             })+
53         };
54     }
55 
56     test_wrapping_to_primitive!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
57 }
58 
59 #[test]
wrapping_is_toprimitive()60 fn wrapping_is_toprimitive() {
61     fn require_toprimitive<T: ToPrimitive>(_: &T) {}
62     require_toprimitive(&Wrapping(42));
63 }
64 
65 #[test]
wrapping_is_fromprimitive()66 fn wrapping_is_fromprimitive() {
67     fn require_fromprimitive<T: FromPrimitive>(_: &T) {}
68     require_fromprimitive(&Wrapping(42));
69 }
70 
71 #[test]
wrapping_is_numcast()72 fn wrapping_is_numcast() {
73     fn require_numcast<T: NumCast>(_: &T) {}
74     require_numcast(&Wrapping(42));
75 }
76 
77 #[test]
as_primitive()78 fn as_primitive() {
79     let x: f32 = (1.625f64).as_();
80     assert_eq!(x, 1.625f32);
81 
82     let x: f32 = (3.14159265358979323846f64).as_();
83     assert_eq!(x, 3.1415927f32);
84 
85     let x: u8 = (768i16).as_();
86     assert_eq!(x, 0);
87 }
88 
89 #[test]
float_to_integer_checks_overflow()90 fn float_to_integer_checks_overflow() {
91     // This will overflow an i32
92     let source: f64 = 1.0e+123f64;
93 
94     // Expect the overflow to be caught
95     assert_eq!(cast::<f64, i32>(source), None);
96 }
97 
98 #[test]
cast_to_int_checks_overflow()99 fn cast_to_int_checks_overflow() {
100     let big_f: f64 = 1.0e123;
101     let normal_f: f64 = 1.0;
102     let small_f: f64 = -1.0e123;
103     assert_eq!(None, cast::<f64, isize>(big_f));
104     assert_eq!(None, cast::<f64, i8>(big_f));
105     assert_eq!(None, cast::<f64, i16>(big_f));
106     assert_eq!(None, cast::<f64, i32>(big_f));
107     assert_eq!(None, cast::<f64, i64>(big_f));
108 
109     assert_eq!(Some(normal_f as isize), cast::<f64, isize>(normal_f));
110     assert_eq!(Some(normal_f as i8), cast::<f64, i8>(normal_f));
111     assert_eq!(Some(normal_f as i16), cast::<f64, i16>(normal_f));
112     assert_eq!(Some(normal_f as i32), cast::<f64, i32>(normal_f));
113     assert_eq!(Some(normal_f as i64), cast::<f64, i64>(normal_f));
114 
115     assert_eq!(None, cast::<f64, isize>(small_f));
116     assert_eq!(None, cast::<f64, i8>(small_f));
117     assert_eq!(None, cast::<f64, i16>(small_f));
118     assert_eq!(None, cast::<f64, i32>(small_f));
119     assert_eq!(None, cast::<f64, i64>(small_f));
120 }
121 
122 #[test]
cast_to_unsigned_int_checks_overflow()123 fn cast_to_unsigned_int_checks_overflow() {
124     let big_f: f64 = 1.0e123;
125     let normal_f: f64 = 1.0;
126     let small_f: f64 = -1.0e123;
127     assert_eq!(None, cast::<f64, usize>(big_f));
128     assert_eq!(None, cast::<f64, u8>(big_f));
129     assert_eq!(None, cast::<f64, u16>(big_f));
130     assert_eq!(None, cast::<f64, u32>(big_f));
131     assert_eq!(None, cast::<f64, u64>(big_f));
132 
133     assert_eq!(Some(normal_f as usize), cast::<f64, usize>(normal_f));
134     assert_eq!(Some(normal_f as u8), cast::<f64, u8>(normal_f));
135     assert_eq!(Some(normal_f as u16), cast::<f64, u16>(normal_f));
136     assert_eq!(Some(normal_f as u32), cast::<f64, u32>(normal_f));
137     assert_eq!(Some(normal_f as u64), cast::<f64, u64>(normal_f));
138 
139     assert_eq!(None, cast::<f64, usize>(small_f));
140     assert_eq!(None, cast::<f64, u8>(small_f));
141     assert_eq!(None, cast::<f64, u16>(small_f));
142     assert_eq!(None, cast::<f64, u32>(small_f));
143     assert_eq!(None, cast::<f64, u64>(small_f));
144 }
145 
146 #[test]
147 #[cfg(has_i128)]
cast_to_i128_checks_overflow()148 fn cast_to_i128_checks_overflow() {
149     let big_f: f64 = 1.0e123;
150     let normal_f: f64 = 1.0;
151     let small_f: f64 = -1.0e123;
152     assert_eq!(None, cast::<f64, i128>(big_f));
153     assert_eq!(None, cast::<f64, u128>(big_f));
154 
155     assert_eq!(Some(normal_f as i128), cast::<f64, i128>(normal_f));
156     assert_eq!(Some(normal_f as u128), cast::<f64, u128>(normal_f));
157 
158     assert_eq!(None, cast::<f64, i128>(small_f));
159     assert_eq!(None, cast::<f64, u128>(small_f));
160 }
161 
162 #[cfg(feature = "std")]
dbg(args: ::core::fmt::Arguments)163 fn dbg(args: ::core::fmt::Arguments) {
164     println!("{}", args);
165 }
166 
167 #[cfg(not(feature = "std"))]
dbg(_: ::core::fmt::Arguments)168 fn dbg(_: ::core::fmt::Arguments) {}
169 
170 // Rust 1.8 doesn't handle cfg on macros correctly
171 macro_rules! dbg { ($($tok:tt)*) => { dbg(format_args!($($tok)*)) } }
172 
173 macro_rules! float_test_edge {
174     ($f:ident -> $($t:ident)+) => { $({
175         dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
176 
177         let small = if $t::MIN == 0 || mem::size_of::<$t>() < mem::size_of::<$f>() {
178             $t::MIN as $f - 1.0
179         } else {
180             ($t::MIN as $f).raw_offset(1).floor()
181         };
182         let fmin = small.raw_offset(-1);
183         dbg!("  testing min {}\n\tvs. {:.0}\n\tand {:.0}", $t::MIN, fmin, small);
184         assert_eq!(Some($t::MIN), cast::<$f, $t>($t::MIN as $f));
185         assert_eq!(Some($t::MIN), cast::<$f, $t>(fmin));
186         assert_eq!(None, cast::<$f, $t>(small));
187 
188         let (max, large) = if mem::size_of::<$t>() < mem::size_of::<$f>() {
189             ($t::MAX, $t::MAX as $f + 1.0)
190         } else {
191             let large = $t::MAX as $f; // rounds up!
192             let max = large.raw_offset(-1) as $t; // the next smallest possible
193             assert_eq!(max.count_ones(), $f::MANTISSA_DIGITS);
194             (max, large)
195         };
196         let fmax = large.raw_offset(-1);
197         dbg!("  testing max {}\n\tvs. {:.0}\n\tand {:.0}", max, fmax, large);
198         assert_eq!(Some(max), cast::<$f, $t>(max as $f));
199         assert_eq!(Some(max), cast::<$f, $t>(fmax));
200         assert_eq!(None, cast::<$f, $t>(large));
201 
202         dbg!("  testing non-finite values");
203         assert_eq!(None, cast::<$f, $t>($f::NAN));
204         assert_eq!(None, cast::<$f, $t>($f::INFINITY));
205         assert_eq!(None, cast::<$f, $t>($f::NEG_INFINITY));
206     })+}
207 }
208 
209 trait RawOffset: Sized {
210     type Raw;
raw_offset(self, offset: Self::Raw) -> Self211     fn raw_offset(self, offset: Self::Raw) -> Self;
212 }
213 
214 impl RawOffset for f32 {
215     type Raw = i32;
raw_offset(self, offset: Self::Raw) -> Self216     fn raw_offset(self, offset: Self::Raw) -> Self {
217         unsafe {
218             let raw: Self::Raw = mem::transmute(self);
219             mem::transmute(raw + offset)
220         }
221     }
222 }
223 
224 impl RawOffset for f64 {
225     type Raw = i64;
raw_offset(self, offset: Self::Raw) -> Self226     fn raw_offset(self, offset: Self::Raw) -> Self {
227         unsafe {
228             let raw: Self::Raw = mem::transmute(self);
229             mem::transmute(raw + offset)
230         }
231     }
232 }
233 
234 #[test]
cast_float_to_int_edge_cases()235 fn cast_float_to_int_edge_cases() {
236     float_test_edge!(f32 -> isize i8 i16 i32 i64);
237     float_test_edge!(f32 -> usize u8 u16 u32 u64);
238     float_test_edge!(f64 -> isize i8 i16 i32 i64);
239     float_test_edge!(f64 -> usize u8 u16 u32 u64);
240 }
241 
242 #[test]
243 #[cfg(has_i128)]
cast_float_to_i128_edge_cases()244 fn cast_float_to_i128_edge_cases() {
245     float_test_edge!(f32 -> i128 u128);
246     float_test_edge!(f64 -> i128 u128);
247 }
248 
249 macro_rules! int_test_edge {
250     ($f:ident -> { $($t:ident)+ } with $BigS:ident $BigU:ident ) => { $({
251         fn test_edge() {
252             dbg!("testing cast edge cases for {} -> {}", stringify!($f), stringify!($t));
253 
254             match ($f::MIN as $BigS).cmp(&($t::MIN as $BigS)) {
255                 Greater => {
256                     assert_eq!(Some($f::MIN as $t), cast::<$f, $t>($f::MIN));
257                 }
258                 Equal => {
259                     assert_eq!(Some($t::MIN), cast::<$f, $t>($f::MIN));
260                 }
261                 Less => {
262                     let min = $t::MIN as $f;
263                     assert_eq!(Some($t::MIN), cast::<$f, $t>(min));
264                     assert_eq!(None, cast::<$f, $t>(min - 1));
265                 }
266             }
267 
268             match ($f::MAX as $BigU).cmp(&($t::MAX as $BigU)) {
269                 Greater => {
270                     let max = $t::MAX as $f;
271                     assert_eq!(Some($t::MAX), cast::<$f, $t>(max));
272                     assert_eq!(None, cast::<$f, $t>(max + 1));
273                 }
274                 Equal => {
275                     assert_eq!(Some($t::MAX), cast::<$f, $t>($f::MAX));
276                 }
277                 Less => {
278                     assert_eq!(Some($f::MAX as $t), cast::<$f, $t>($f::MAX));
279                 }
280             }
281         }
282         test_edge();
283     })+}
284 }
285 
286 #[test]
cast_int_to_int_edge_cases()287 fn cast_int_to_int_edge_cases() {
288     use core::cmp::Ordering::*;
289 
290     macro_rules! test_edge {
291         ($( $from:ident )+) => { $({
292             int_test_edge!($from -> { isize i8 i16 i32 i64 } with i64 u64);
293             int_test_edge!($from -> { usize u8 u16 u32 u64 } with i64 u64);
294         })+}
295     }
296 
297     test_edge!(isize i8 i16 i32 i64);
298     test_edge!(usize u8 u16 u32 u64);
299 }
300 
301 #[test]
302 #[cfg(has_i128)]
cast_int_to_128_edge_cases()303 fn cast_int_to_128_edge_cases() {
304     use core::cmp::Ordering::*;
305 
306     macro_rules! test_edge {
307         ($( $t:ident )+) => {
308             $(
309                 int_test_edge!($t -> { i128 u128 } with i128 u128);
310             )+
311             int_test_edge!(i128 -> { $( $t )+ } with i128 u128);
312             int_test_edge!(u128 -> { $( $t )+ } with i128 u128);
313         }
314     }
315 
316     test_edge!(isize i8 i16 i32 i64 i128);
317     test_edge!(usize u8 u16 u32 u64 u128);
318 }
319 
320