From f99aa0e1812bc5de88da44fb6c28e79c5ccd858a Mon Sep 17 00:00:00 2001 From: Dan Barella Date: Sat, 27 Jan 2018 15:41:06 -0500 Subject: [PATCH] Check overflow when casting floats to integers. This change adds some new macro rules used when converting from floats to integers. There are two macro rule variants, one for signed ints, one for unsigned ints. Among other things, this change specifically addresses the overflow case documented in https://github.com/rust-num/num-traits/issues/12 --- src/cast.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/src/cast.rs b/src/cast.rs index 630341f..5f45bd5 100644 --- a/src/cast.rs +++ b/src/cast.rs @@ -238,35 +238,93 @@ macro_rules! impl_to_primitive_float_to_float { ) } +macro_rules! impl_to_primitive_float_to_signed_int { + ($SrcT:ident, $DstT:ident, $slf:expr) => ( + if size_of::<$SrcT>() <= size_of::<$DstT>() { + Some($slf as $DstT) + } else { + // Make sure the value is in range for the cast. + let n = $slf as $DstT; + let max_value: $DstT = ::std::$DstT::MAX; + if -max_value as $DstT <= n && n <= max_value as $DstT { + Some($slf as $DstT) + } else { + None + } + } + ) +} + +macro_rules! impl_to_primitive_float_to_unsigned_int { + ($SrcT:ident, $DstT:ident, $slf:expr) => ( + if size_of::<$SrcT>() <= size_of::<$DstT>() { + Some($slf as $DstT) + } else { + // Make sure the value is in range for the cast. + let n = $slf as $DstT; + let max_value: $DstT = ::std::$DstT::MAX; + if n <= max_value as $DstT { + Some($slf as $DstT) + } else { + None + } + } + ) +} + macro_rules! impl_to_primitive_float { ($T:ident) => ( impl ToPrimitive for $T { #[inline] - fn to_isize(&self) -> Option { Some(*self as isize) } + fn to_isize(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, isize, *self) + } #[inline] - fn to_i8(&self) -> Option { Some(*self as i8) } + fn to_i8(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i8, *self) + } #[inline] - fn to_i16(&self) -> Option { Some(*self as i16) } + fn to_i16(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i16, *self) + } #[inline] - fn to_i32(&self) -> Option { Some(*self as i32) } + fn to_i32(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i32, *self) + } #[inline] - fn to_i64(&self) -> Option { Some(*self as i64) } + fn to_i64(&self) -> Option { + impl_to_primitive_float_to_signed_int!($T, i64, *self) + } #[inline] - fn to_usize(&self) -> Option { Some(*self as usize) } + fn to_usize(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, usize, *self) + } #[inline] - fn to_u8(&self) -> Option { Some(*self as u8) } + fn to_u8(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u8, *self) + } #[inline] - fn to_u16(&self) -> Option { Some(*self as u16) } + fn to_u16(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u16, *self) + } #[inline] - fn to_u32(&self) -> Option { Some(*self as u32) } + fn to_u32(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u32, *self) + } #[inline] - fn to_u64(&self) -> Option { Some(*self as u64) } + fn to_u64(&self) -> Option { + impl_to_primitive_float_to_unsigned_int!($T, u64, *self) + } #[inline] - fn to_f32(&self) -> Option { impl_to_primitive_float_to_float!($T, f32, *self) } + fn to_f32(&self) -> Option { + impl_to_primitive_float_to_float!($T, f32, *self) + } #[inline] - fn to_f64(&self) -> Option { impl_to_primitive_float_to_float!($T, f64, *self) } + fn to_f64(&self) -> Option { + impl_to_primitive_float_to_float!($T, f64, *self) + } } ) } @@ -591,3 +649,17 @@ fn as_primitive() { let x: u8 = (768i16).as_(); assert_eq!(x, 0); } + +#[test] +fn float_to_integer_checks_overflow() { + // This will overflow an i32 + let source: f64 = 1.0e+123f64; + + // Expect the overflow to be caught + assert_eq!(source.to_i32(), None); + + // Specifically make sure we didn't silently let the overflow through + // (This line is mostly for humans -- the robots should catch any problem + // on the line above). + assert_ne!(source.to_i32(), Some(-2147483648)); +}