Further simplify float-to-int range checks
We don't actually need to compute the `trunc()` value, as long as we can figure out the right values for the exclusive range `(MIN-1, MAX+1)` to measure the same truncation effect.
This commit is contained in:
parent
f0ed42b3bc
commit
a4d234c253
53
src/cast.rs
53
src/cast.rs
|
@ -220,18 +220,23 @@ macro_rules! impl_to_primitive_float_to_signed_int {
|
||||||
($f:ident : $( fn $method:ident -> $i:ident ; )*) => {$(
|
($f:ident : $( fn $method:ident -> $i:ident ; )*) => {$(
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $method(&self) -> Option<$i> {
|
fn $method(&self) -> Option<$i> {
|
||||||
let t = self.trunc(); // round toward zero.
|
// Float as int truncates toward zero, so we want to allow values
|
||||||
// MIN is a power of two, which we can cast and compare directly.
|
// in the exclusive range `(MIN-1, MAX+1)`.
|
||||||
if t >= $i::MIN as $f {
|
if size_of::<$f>() > size_of::<$i>() {
|
||||||
// The mantissa might not be able to represent all digits of MAX.
|
// With a larger size, we can represent the range exactly.
|
||||||
let sig_bits = size_of::<$i>() as u32 * 8 - 1;
|
const MIN_M1: $f = $i::MIN as $f - 1.0;
|
||||||
let max = if sig_bits > $f::MANTISSA_DIGITS {
|
const MAX_P1: $f = $i::MAX as $f + 1.0;
|
||||||
let lost_bits = sig_bits - $f::MANTISSA_DIGITS;
|
if *self > MIN_M1 && *self < MAX_P1 {
|
||||||
$i::MAX & !((1 << lost_bits) - 1)
|
return Some(*self as $i);
|
||||||
} else {
|
}
|
||||||
$i::MAX
|
} else {
|
||||||
};
|
// We can't represent `MIN-1` exactly, but there's no fractional part
|
||||||
if t <= max as $f {
|
// at this magnitude, so we can just use a `MIN` inclusive boundary.
|
||||||
|
const MIN: $f = $i::MIN as $f;
|
||||||
|
// We can't represent `MAX` exactly, but it will round up to exactly
|
||||||
|
// `MAX+1` (a power of two) when we cast it.
|
||||||
|
const MAX_P1: $f = $i::MAX as $f;
|
||||||
|
if *self >= MIN && *self < MAX_P1 {
|
||||||
return Some(*self as $i);
|
return Some(*self as $i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,17 +249,19 @@ macro_rules! impl_to_primitive_float_to_unsigned_int {
|
||||||
($f:ident : $( fn $method:ident -> $u:ident ; )*) => {$(
|
($f:ident : $( fn $method:ident -> $u:ident ; )*) => {$(
|
||||||
#[inline]
|
#[inline]
|
||||||
fn $method(&self) -> Option<$u> {
|
fn $method(&self) -> Option<$u> {
|
||||||
let t = self.trunc(); // round toward zero.
|
// Float as int truncates toward zero, so we want to allow values
|
||||||
if t >= 0.0 {
|
// in the exclusive range `(-1, MAX+1)`.
|
||||||
// The mantissa might not be able to represent all digits of MAX.
|
if size_of::<$f>() > size_of::<$u>() {
|
||||||
let sig_bits = size_of::<$u>() as u32 * 8;
|
// With a larger size, we can represent the range exactly.
|
||||||
let max = if sig_bits > $f::MANTISSA_DIGITS {
|
const MAX_P1: $f = $u::MAX as $f + 1.0;
|
||||||
let lost_bits = sig_bits - $f::MANTISSA_DIGITS;
|
if *self > -1.0 && *self < MAX_P1 {
|
||||||
$u::MAX & !((1 << lost_bits) - 1)
|
return Some(*self as $u);
|
||||||
} else {
|
}
|
||||||
$u::MAX
|
} else {
|
||||||
};
|
// We can't represent `MAX` exactly, but it will round up to exactly
|
||||||
if t <= max as $f {
|
// `MAX+1` (a power of two) when we cast it.
|
||||||
|
const MAX_P1: $f = $u::MAX as $f;
|
||||||
|
if *self > -1.0 && *self < MAX_P1 {
|
||||||
return Some(*self as $u);
|
return Some(*self as $u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue