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
This commit is contained in:
parent
aa36cdb206
commit
f99aa0e181
96
src/cast.rs
96
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));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue