Skip to content

Commit a4d234c

Browse files
committed
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.
1 parent f0ed42b commit a4d234c

File tree

1 file changed

+30
-23
lines changed

1 file changed

+30
-23
lines changed

src/cast.rs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -220,18 +220,23 @@ macro_rules! impl_to_primitive_float_to_signed_int {
220220
($f:ident : $( fn $method:ident -> $i:ident ; )*) => {$(
221221
#[inline]
222222
fn $method(&self) -> Option<$i> {
223-
let t = self.trunc(); // round toward zero.
224-
// MIN is a power of two, which we can cast and compare directly.
225-
if t >= $i::MIN as $f {
226-
// The mantissa might not be able to represent all digits of MAX.
227-
let sig_bits = size_of::<$i>() as u32 * 8 - 1;
228-
let max = if sig_bits > $f::MANTISSA_DIGITS {
229-
let lost_bits = sig_bits - $f::MANTISSA_DIGITS;
230-
$i::MAX & !((1 << lost_bits) - 1)
231-
} else {
232-
$i::MAX
233-
};
234-
if t <= max as $f {
223+
// Float as int truncates toward zero, so we want to allow values
224+
// in the exclusive range `(MIN-1, MAX+1)`.
225+
if size_of::<$f>() > size_of::<$i>() {
226+
// With a larger size, we can represent the range exactly.
227+
const MIN_M1: $f = $i::MIN as $f - 1.0;
228+
const MAX_P1: $f = $i::MAX as $f + 1.0;
229+
if *self > MIN_M1 && *self < MAX_P1 {
230+
return Some(*self as $i);
231+
}
232+
} else {
233+
// We can't represent `MIN-1` exactly, but there's no fractional part
234+
// at this magnitude, so we can just use a `MIN` inclusive boundary.
235+
const MIN: $f = $i::MIN as $f;
236+
// We can't represent `MAX` exactly, but it will round up to exactly
237+
// `MAX+1` (a power of two) when we cast it.
238+
const MAX_P1: $f = $i::MAX as $f;
239+
if *self >= MIN && *self < MAX_P1 {
235240
return Some(*self as $i);
236241
}
237242
}
@@ -244,17 +249,19 @@ macro_rules! impl_to_primitive_float_to_unsigned_int {
244249
($f:ident : $( fn $method:ident -> $u:ident ; )*) => {$(
245250
#[inline]
246251
fn $method(&self) -> Option<$u> {
247-
let t = self.trunc(); // round toward zero.
248-
if t >= 0.0 {
249-
// The mantissa might not be able to represent all digits of MAX.
250-
let sig_bits = size_of::<$u>() as u32 * 8;
251-
let max = if sig_bits > $f::MANTISSA_DIGITS {
252-
let lost_bits = sig_bits - $f::MANTISSA_DIGITS;
253-
$u::MAX & !((1 << lost_bits) - 1)
254-
} else {
255-
$u::MAX
256-
};
257-
if t <= max as $f {
252+
// Float as int truncates toward zero, so we want to allow values
253+
// in the exclusive range `(-1, MAX+1)`.
254+
if size_of::<$f>() > size_of::<$u>() {
255+
// With a larger size, we can represent the range exactly.
256+
const MAX_P1: $f = $u::MAX as $f + 1.0;
257+
if *self > -1.0 && *self < MAX_P1 {
258+
return Some(*self as $u);
259+
}
260+
} else {
261+
// We can't represent `MAX` exactly, but it will round up to exactly
262+
// `MAX+1` (a power of two) when we cast it.
263+
const MAX_P1: $f = $u::MAX as $f;
264+
if *self > -1.0 && *self < MAX_P1 {
258265
return Some(*self as $u);
259266
}
260267
}

0 commit comments

Comments
 (0)