#[inline]
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub(crate) const fn isize_to_f32(v: isize) -> f32 {
v as f32
}
#[inline]
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub(crate) const fn usize_to_f32(v: usize) -> f32 {
v as f32
}
#[inline]
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub(crate) const fn i32_to_f32(v: i32) -> f32 {
v as f32
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub(crate) const fn f32_to_isize(v: f32) -> isize {
v as isize
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)]
pub(crate) const fn f32_to_i32(v: f32) -> i32 {
v as i32
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub(crate) const fn f32_to_u32(v: f32) -> u32 {
v as u32
}
#[cfg(test)]
#[allow(clippy::float_cmp, clippy::unreadable_literal)]
mod autotest_generated {
use super::*;
const TWO_POW_24: i32 = 16_777_216;
const TWO_POW_31: f32 = 2_147_483_648.0;
const TWO_POW_32: f32 = 4_294_967_296.0;
#[test]
fn zero_maps_to_positive_zero_in_both_directions() {
assert_eq!(isize_to_f32(0).to_bits(), 0_u32);
assert_eq!(usize_to_f32(0).to_bits(), 0_u32);
assert_eq!(i32_to_f32(0).to_bits(), 0_u32);
assert_eq!(f32_to_isize(0.0), 0);
assert_eq!(f32_to_i32(0.0), 0);
assert_eq!(f32_to_u32(0.0), 0);
}
#[test]
fn negative_zero_converts_to_integer_zero() {
assert_eq!(f32_to_isize(-0.0), 0);
assert_eq!(f32_to_i32(-0.0), 0);
assert_eq!(f32_to_u32(-0.0), 0);
}
#[test]
fn nan_saturates_to_zero_not_a_panic() {
assert_eq!(f32_to_isize(f32::NAN), 0);
assert_eq!(f32_to_i32(f32::NAN), 0);
assert_eq!(f32_to_u32(f32::NAN), 0);
assert_eq!(f32_to_i32(-f32::NAN), 0);
assert_eq!(f32_to_u32(-f32::NAN), 0);
let payload_nan = f32::from_bits(0x7fc0_1234);
assert!(payload_nan.is_nan());
assert_eq!(f32_to_i32(payload_nan), 0);
assert_eq!(f32_to_u32(payload_nan), 0);
}
#[test]
fn infinities_saturate_to_the_integer_bounds() {
assert_eq!(f32_to_isize(f32::INFINITY), isize::MAX);
assert_eq!(f32_to_isize(f32::NEG_INFINITY), isize::MIN);
assert_eq!(f32_to_i32(f32::INFINITY), i32::MAX);
assert_eq!(f32_to_i32(f32::NEG_INFINITY), i32::MIN);
assert_eq!(f32_to_u32(f32::INFINITY), u32::MAX);
assert_eq!(f32_to_u32(f32::NEG_INFINITY), 0);
}
#[test]
fn out_of_range_floats_saturate_rather_than_wrap() {
assert_eq!(f32_to_i32(TWO_POW_31), i32::MAX);
assert_eq!(f32_to_i32(-TWO_POW_31 - 256.0), i32::MIN);
assert_eq!(f32_to_i32(1.0e30), i32::MAX);
assert_eq!(f32_to_i32(-1.0e30), i32::MIN);
assert_eq!(f32_to_i32(f32::MAX), i32::MAX);
assert_eq!(f32_to_i32(f32::MIN), i32::MIN);
assert_eq!(f32_to_u32(TWO_POW_32), u32::MAX);
assert_eq!(f32_to_u32(1.0e30), u32::MAX);
assert_eq!(f32_to_u32(f32::MAX), u32::MAX);
assert_eq!(f32_to_isize(1.0e30), isize::MAX);
assert_eq!(f32_to_isize(-1.0e30), isize::MIN);
assert_eq!(f32_to_isize(f32::MAX), isize::MAX);
assert_eq!(f32_to_isize(f32::MIN), isize::MIN);
}
#[test]
fn negatives_clamp_to_zero_for_the_unsigned_cast() {
assert_eq!(f32_to_u32(-1.0), 0);
assert_eq!(f32_to_u32(-0.5), 0);
assert_eq!(f32_to_u32(-1.0e30), 0);
assert_eq!(f32_to_u32(-f32::from_bits(1)), 0); }
#[test]
fn largest_in_range_floats_convert_exactly() {
assert_eq!(f32_to_i32(2_147_483_520.0), 2_147_483_520);
assert_eq!(f32_to_u32(4_294_967_040.0), 4_294_967_040);
}
#[test]
fn fractional_values_truncate_toward_zero() {
assert_eq!(f32_to_i32(1.9), 1);
assert_eq!(f32_to_i32(-1.9), -1); assert_eq!(f32_to_i32(0.9), 0);
assert_eq!(f32_to_i32(-0.9), 0);
assert_eq!(f32_to_isize(-1.9), -1);
assert_eq!(f32_to_u32(1.9), 1);
assert_eq!(f32_to_u32(0.9), 0);
}
#[test]
fn subnormal_and_tiny_magnitudes_flush_to_zero() {
assert_eq!(f32_to_i32(f32::MIN_POSITIVE), 0);
assert_eq!(f32_to_u32(f32::MIN_POSITIVE), 0);
assert_eq!(f32_to_isize(f32::from_bits(1)), 0); assert_eq!(f32_to_i32(f32::EPSILON), 0);
}
#[test]
fn integers_up_to_two_pow_24_are_exact() {
assert_eq!(i32_to_f32(TWO_POW_24), 16_777_216.0);
assert_eq!(f32_to_i32(i32_to_f32(TWO_POW_24)), TWO_POW_24);
assert_eq!(i32_to_f32(TWO_POW_24 - 1), 16_777_215.0);
assert_eq!(usize_to_f32(TWO_POW_24 as usize), 16_777_216.0);
assert_eq!(isize_to_f32(TWO_POW_24 as isize), 16_777_216.0);
}
#[test]
fn just_above_two_pow_24_loses_precision_by_round_to_even() {
assert_eq!(i32_to_f32(TWO_POW_24 + 1), 16_777_216.0);
assert_eq!(i32_to_f32(TWO_POW_24 + 3), 16_777_220.0);
assert_eq!(usize_to_f32(TWO_POW_24 as usize + 1), 16_777_216.0);
assert_eq!(isize_to_f32(TWO_POW_24 as isize + 1), 16_777_216.0);
assert_eq!(f32_to_i32(i32_to_f32(TWO_POW_24 + 1)), TWO_POW_24);
}
#[test]
fn int_min_max_convert_without_panic_and_stay_finite() {
for v in [i32::MIN, i32::MIN + 1, -1, 0, 1, i32::MAX - 1, i32::MAX] {
assert!(i32_to_f32(v).is_finite());
}
for v in [isize::MIN, isize::MIN + 1, -1, 0, 1, isize::MAX - 1, isize::MAX] {
assert!(isize_to_f32(v).is_finite());
}
for v in [0_usize, 1, usize::MAX - 1, usize::MAX] {
assert!(usize_to_f32(v).is_finite());
}
assert_eq!(i32_to_f32(i32::MIN), -TWO_POW_31);
assert_eq!(i32_to_f32(i32::MAX), TWO_POW_31);
assert!(i32_to_f32(i32::MAX) > 2_147_483_520.0);
}
#[test]
fn unsigned_max_does_not_go_negative_or_infinite() {
let m = usize_to_f32(usize::MAX);
assert!(m.is_finite());
assert!(m.is_sign_positive());
assert!(m > 0.0);
}
#[cfg(target_pointer_width = "64")]
#[test]
fn pointer_sized_extremes_round_to_the_adjacent_power_of_two() {
assert_eq!(isize_to_f32(isize::MAX), 9_223_372_036_854_775_808.0); assert_eq!(isize_to_f32(isize::MIN), -9_223_372_036_854_775_808.0);
assert_eq!(usize_to_f32(usize::MAX), 18_446_744_073_709_551_616.0); }
#[test]
fn round_trip_is_exact_within_the_exactly_representable_range() {
let mut v: i32 = -TWO_POW_24;
while v <= TWO_POW_24 {
assert_eq!(f32_to_i32(i32_to_f32(v)), v, "i32 round-trip broke at {v}");
assert_eq!(f32_to_isize(isize_to_f32(v as isize)), v as isize);
v = v.saturating_add(4093); }
for v in (TWO_POW_24 - 512)..=TWO_POW_24 {
assert_eq!(f32_to_i32(i32_to_f32(v)), v);
assert_eq!(f32_to_u32(i32_to_f32(v)), v as u32);
}
}
#[test]
fn round_trip_survives_the_int_extremes_via_saturation() {
assert_eq!(f32_to_i32(i32_to_f32(i32::MAX)), i32::MAX);
assert_eq!(f32_to_i32(i32_to_f32(i32::MIN)), i32::MIN);
assert_eq!(f32_to_isize(isize_to_f32(isize::MAX)), isize::MAX);
assert_eq!(f32_to_isize(isize_to_f32(isize::MIN)), isize::MIN);
}
#[test]
fn powers_of_two_round_trip_exactly() {
for exp in 0..31_u32 {
let v = 1_i32 << exp;
assert_eq!(f32_to_i32(i32_to_f32(v)), v, "2^{exp} round-trip broke");
assert_eq!(f32_to_i32(i32_to_f32(-v)), -v);
assert_eq!(f32_to_u32(i32_to_f32(v)), v as u32);
}
}
#[test]
fn int_to_float_is_monotonic() {
let samples = [
isize::MIN,
-1_000_000_000,
-1,
0,
1,
TWO_POW_24 as isize,
1_000_000_000,
isize::MAX,
];
for w in samples.windows(2) {
assert!(
isize_to_f32(w[0]) <= isize_to_f32(w[1]),
"monotonicity broke between {} and {}",
w[0],
w[1]
);
}
}
#[test]
fn signed_and_unsigned_paths_agree_where_the_ranges_overlap() {
for v in [0.0_f32, 1.0, 0.5, 42.7, 65_535.0, 16_777_216.0, 2_147_483_520.0] {
assert_eq!(f32_to_u32(v), f32_to_i32(v) as u32, "disagreement at {v}");
assert_eq!(f32_to_isize(v), f32_to_i32(v) as isize);
}
for v in [0_i32, 1, -1, 12_345, i32::MIN, i32::MAX] {
assert_eq!(i32_to_f32(v), isize_to_f32(v as isize), "disagreement at {v}");
}
}
#[test]
fn sign_is_preserved_by_the_int_to_float_casts() {
assert!(isize_to_f32(-1).is_sign_negative());
assert!(isize_to_f32(1).is_sign_positive());
assert!(i32_to_f32(i32::MIN).is_sign_negative());
assert!(usize_to_f32(usize::MAX).is_sign_positive());
}
#[test]
fn usable_in_const_context_with_the_same_saturating_semantics() {
const NAN_I32: i32 = f32_to_i32(f32::NAN);
const INF_I32: i32 = f32_to_i32(f32::INFINITY);
const NEG_U32: u32 = f32_to_u32(-5.0);
const BIG_ISIZE: isize = f32_to_isize(1.0e30);
const FROM_I32: f32 = i32_to_f32(-42);
const FROM_USIZE: f32 = usize_to_f32(42);
const FROM_ISIZE: f32 = isize_to_f32(-42);
assert_eq!(NAN_I32, 0);
assert_eq!(INF_I32, i32::MAX);
assert_eq!(NEG_U32, 0);
assert_eq!(BIG_ISIZE, isize::MAX);
assert_eq!(FROM_I32, -42.0);
assert_eq!(FROM_USIZE, 42.0);
assert_eq!(FROM_ISIZE, -42.0);
}
}