Rewrite range checks in float ToPrimitive macros
This commit is contained in:
parent
8e27c7327d
commit
b025c273c7
63
src/cast.rs
63
src/cast.rs
|
@ -1,3 +1,6 @@
|
||||||
|
use core::{i8, i16, i32, i64, isize};
|
||||||
|
use core::{u8, u16, u32, u64, usize};
|
||||||
|
use core::{f32, f64};
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use core::num::Wrapping;
|
use core::num::Wrapping;
|
||||||
|
|
||||||
|
@ -220,43 +223,57 @@ impl_to_primitive_uint!(u32);
|
||||||
impl_to_primitive_uint!(u64);
|
impl_to_primitive_uint!(u64);
|
||||||
|
|
||||||
macro_rules! impl_to_primitive_float_to_float {
|
macro_rules! impl_to_primitive_float_to_float {
|
||||||
($SrcT:ident, $DstT:ident, $slf:expr) => (
|
($SrcT:ident, $DstT:ident, $slf:expr) => ({
|
||||||
if size_of::<$SrcT>() <= size_of::<$DstT>() {
|
// Only finite values that are reducing size need to worry about overflow.
|
||||||
Some($slf as $DstT)
|
if size_of::<$SrcT>() > size_of::<$DstT>() && FloatCore::is_finite($slf) {
|
||||||
} else {
|
|
||||||
// Make sure the value is in range for the cast.
|
|
||||||
// NaN and +-inf are cast as they are.
|
|
||||||
let n = $slf as f64;
|
let n = $slf as f64;
|
||||||
let max_value: $DstT = ::core::$DstT::MAX;
|
if n < $DstT::MIN as f64 || n > $DstT::MAX as f64 {
|
||||||
if !FloatCore::is_finite(n) || (-max_value as f64 <= n && n <= max_value as f64)
|
return None;
|
||||||
{
|
|
||||||
Some($slf as $DstT)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
// We can safely cast NaN, +-inf, and finite values in range.
|
||||||
|
Some($slf as $DstT)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_to_primitive_float_to_signed_int {
|
macro_rules! impl_to_primitive_float_to_signed_int {
|
||||||
($SrcT:ident, $DstT:ident, $slf:expr) => ({
|
($SrcT:ident, $DstT:ident, $slf:expr) => ({
|
||||||
let t = $slf.trunc();
|
let t = $slf.trunc(); // round toward zero.
|
||||||
if t >= $DstT::min_value() as $SrcT && t <= $DstT::max_value() as $SrcT {
|
// MIN is a power of two, which we can cast and compare directly.
|
||||||
Some($slf as $DstT)
|
if t >= $DstT::MIN as $SrcT {
|
||||||
} else {
|
// The mantissa might not be able to represent all digits of MAX.
|
||||||
None
|
let sig_bits = size_of::<$DstT>() as u32 * 8 - 1;
|
||||||
|
let max = if sig_bits > $SrcT::MANTISSA_DIGITS {
|
||||||
|
let lost_bits = sig_bits - $SrcT::MANTISSA_DIGITS;
|
||||||
|
$DstT::MAX & !((1 << lost_bits) - 1)
|
||||||
|
} else {
|
||||||
|
$DstT::MAX
|
||||||
|
};
|
||||||
|
if t <= max as $SrcT {
|
||||||
|
return Some($slf as $DstT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_to_primitive_float_to_unsigned_int {
|
macro_rules! impl_to_primitive_float_to_unsigned_int {
|
||||||
($SrcT:ident, $DstT:ident, $slf:expr) => ({
|
($SrcT:ident, $DstT:ident, $slf:expr) => ({
|
||||||
let t = $slf.trunc();
|
let t = $slf.trunc(); // round toward zero.
|
||||||
if t >= $DstT::min_value() as $SrcT && t <= $DstT::max_value() as $SrcT {
|
if t >= 0.0 {
|
||||||
Some($slf as $DstT)
|
// The mantissa might not be able to represent all digits of MAX.
|
||||||
} else {
|
let sig_bits = size_of::<$DstT>() as u32 * 8;
|
||||||
None
|
let max = if sig_bits > $SrcT::MANTISSA_DIGITS {
|
||||||
|
let lost_bits = sig_bits - $SrcT::MANTISSA_DIGITS;
|
||||||
|
$DstT::MAX & !((1 << lost_bits) - 1)
|
||||||
|
} else {
|
||||||
|
$DstT::MAX
|
||||||
|
};
|
||||||
|
if t <= max as $SrcT {
|
||||||
|
return Some($slf as $DstT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue