瀏覽代碼

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
Dan Barella 7 年之前
父節點
當前提交
f99aa0e181
共有 1 個文件被更改,包括 84 次插入12 次删除
  1. 84 12
      src/cast.rs

+ 84 - 12
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<isize> { Some(*self as isize) }
+            fn to_isize(&self) -> Option<isize> {
+                impl_to_primitive_float_to_signed_int!($T, isize, *self)
+            }
             #[inline]
-            fn to_i8(&self) -> Option<i8> { Some(*self as i8) }
+            fn to_i8(&self) -> Option<i8> {
+                impl_to_primitive_float_to_signed_int!($T, i8, *self)
+            }
             #[inline]
-            fn to_i16(&self) -> Option<i16> { Some(*self as i16) }
+            fn to_i16(&self) -> Option<i16> {
+                impl_to_primitive_float_to_signed_int!($T, i16, *self)
+            }
             #[inline]
-            fn to_i32(&self) -> Option<i32> { Some(*self as i32) }
+            fn to_i32(&self) -> Option<i32> {
+                impl_to_primitive_float_to_signed_int!($T, i32, *self)
+            }
             #[inline]
-            fn to_i64(&self) -> Option<i64> { Some(*self as i64) }
+            fn to_i64(&self) -> Option<i64> {
+                impl_to_primitive_float_to_signed_int!($T, i64, *self)
+            }
 
             #[inline]
-            fn to_usize(&self) -> Option<usize> { Some(*self as usize) }
+            fn to_usize(&self) -> Option<usize> {
+                impl_to_primitive_float_to_unsigned_int!($T, usize, *self)
+            }
             #[inline]
-            fn to_u8(&self) -> Option<u8> { Some(*self as u8) }
+            fn to_u8(&self) -> Option<u8> {
+                impl_to_primitive_float_to_unsigned_int!($T, u8, *self)
+            }
             #[inline]
-            fn to_u16(&self) -> Option<u16> { Some(*self as u16) }
+            fn to_u16(&self) -> Option<u16> {
+                impl_to_primitive_float_to_unsigned_int!($T, u16, *self)
+            }
             #[inline]
-            fn to_u32(&self) -> Option<u32> { Some(*self as u32) }
+            fn to_u32(&self) -> Option<u32> {
+                impl_to_primitive_float_to_unsigned_int!($T, u32, *self)
+            }
             #[inline]
-            fn to_u64(&self) -> Option<u64> { Some(*self as u64) }
+            fn to_u64(&self) -> Option<u64> {
+                impl_to_primitive_float_to_unsigned_int!($T, u64, *self)
+            }
 
             #[inline]
-            fn to_f32(&self) -> Option<f32> { impl_to_primitive_float_to_float!($T, f32, *self) }
+            fn to_f32(&self) -> Option<f32> {
+                impl_to_primitive_float_to_float!($T, f32, *self)
+            }
             #[inline]
-            fn to_f64(&self) -> Option<f64> { impl_to_primitive_float_to_float!($T, f64, *self) }
+            fn to_f64(&self) -> Option<f64> {
+                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));
+}