From c185118776c4f0479baf62208a6c0125aa575a30 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Tue, 28 May 2024 23:04:47 -0400 Subject: [PATCH] Change integer to float conversions to be generic Reduce duplicate code and make it easier to add `f128` operations. --- src/float/conv.rs | 171 ++++++++++++++++++++++++---------------------- src/float/mod.rs | 4 +- src/int/mod.rs | 10 ++- 3 files changed, 101 insertions(+), 84 deletions(-) diff --git a/src/float/conv.rs b/src/float/conv.rs index 52119f3e..2ca8d21e 100644 --- a/src/float/conv.rs +++ b/src/float/conv.rs @@ -10,102 +10,117 @@ use super::Float; /// which unfortunately isn't the easiest kind of code to read. /// /// The algorithm is explained here: -mod int_to_float { - pub fn u32_to_f32_bits(i: u32) -> u32 { - if i == 0 { - return 0; - } - let n = i.leading_zeros(); - let a = (i << n) >> 8; // Significant bits, with bit 24 still in tact. - let b = (i << n) << 24; // Insignificant bits, only relevant for rounding. - let m = a + ((b - (b >> 31 & !a)) >> 31); // Add one when we need to round up. Break ties to even. - let e = 157 - n; // Exponent plus 127, minus one. - (e << 23) + m // + not |, so the mantissa can overflow into the exponent. - } - - pub fn u32_to_f64_bits(i: u32) -> u64 { - if i == 0 { - return 0; - } - let n = i.leading_zeros(); - let m = (i as u64) << (21 + n); // Significant bits, with bit 53 still in tact. - let e = 1053 - n as u64; // Exponent plus 1023, minus one. - (e << 52) + m // Bit 53 of m will overflow into e. - } - - pub fn u64_to_f32_bits(i: u64) -> u32 { - let n = i.leading_zeros(); - let y = i.wrapping_shl(n); - let a = (y >> 40) as u32; // Significant bits, with bit 24 still in tact. - let b = (y >> 8 | y & 0xFFFF) as u32; // Insignificant bits, only relevant for rounding. - let m = a + ((b - (b >> 31 & !a)) >> 31); // Add one when we need to round up. Break ties to even. - let e = if i == 0 { 0 } else { 189 - n }; // Exponent plus 127, minus one, except for zero. - (e << 23) + m // + not |, so the mantissa can overflow into the exponent. - } - - pub fn u64_to_f64_bits(i: u64) -> u64 { - if i == 0 { - return 0; - } - let n = i.leading_zeros(); - let a = (i << n) >> 11; // Significant bits, with bit 53 still in tact. - let b = (i << n) << 53; // Insignificant bits, only relevant for rounding. - let m = a + ((b - (b >> 63 & !a)) >> 63); // Add one when we need to round up. Break ties to even. - let e = 1085 - n as u64; // Exponent plus 1023, minus one. - (e << 52) + m // + not |, so the mantissa can overflow into the exponent. - } - - pub fn u128_to_f32_bits(i: u128) -> u32 { - let n = i.leading_zeros(); - let y = i.wrapping_shl(n); - let a = (y >> 104) as u32; // Significant bits, with bit 24 still in tact. - let b = (y >> 72) as u32 | ((y << 32) >> 32 != 0) as u32; // Insignificant bits, only relevant for rounding. - let m = a + ((b - (b >> 31 & !a)) >> 31); // Add one when we need to round up. Break ties to even. - let e = if i == 0 { 0 } else { 253 - n }; // Exponent plus 127, minus one, except for zero. - (e << 23) + m // + not |, so the mantissa can overflow into the exponent. - } - - pub fn u128_to_f64_bits(i: u128) -> u64 { - let n = i.leading_zeros(); - let y = i.wrapping_shl(n); - let a = (y >> 75) as u64; // Significant bits, with bit 53 still in tact. - let b = (y >> 11 | y & 0xFFFF_FFFF) as u64; // Insignificant bits, only relevant for rounding. - let m = a + ((b - (b >> 63 & !a)) >> 63); // Add one when we need to round up. Break ties to even. - let e = if i == 0 { 0 } else { 1149 - n as u64 }; // Exponent plus 1023, minus one, except for zero. - (e << 52) + m // + not |, so the mantissa can overflow into the exponent. +fn int_to_float_generic(i: I) -> F +where + F: Float, + I: Int, + I::UnsignedInt: CastInto, + F::Int: CastFrom, + F::Int: CastFrom, + F::Int: From, + F::Int: CastInto, +{ + if i == I::ZERO { + return F::ZERO; } + + let sign_bit: F::Int = if I::SIGNED { + F::Int::cast_from(i >> (I::BITS - 1)) << (F::BITS - 1) + } else { + // Never used + F::Int::ZERO + }; + + let i = i.unsigned_abs(); + let n = i.leading_zeros(); + + // Calculate the exponent from the integer's + let e = F::Int::cast_from(I::BITS + F::EXPONENT_BIAS - 2 - n); + + // The mantissa of `i`, still in `I`'s form (left shifted so the first bit is nonzero) + let i_m = i.wrapping_shl(n); + + let m: F::Int = if F::BITS > I::BITS { + F::Int::cast_from(i) << (F::SIGNIFICAND_BITS - I::BITS + 1 + n) + // let m = (i as u64) << (21 + n); // Significant bits, with bit 53 still in tact. + } else { + // Shift the integer into the float's mantissa bits. Keep the lowest + // exponent bit intact. + let m_base = F::Int::cast_from(i_m >> ((I::BITS - F::BITS) + F::EXPONENT_BITS)); + + // Squash the + let dropped_bits: F::Int = if F::BITS == I::BITS { + // Simple case + + // Only the lowest `F::EXPONENT_BITS` bits will be truncated. Shift them + // to the highest position + (i_m << (F::SIGNIFICAND_BITS + 1)).cast() + } else if F::BITS * 2 == I::BITS { + // Specialized case where the float is half the integer size + + // The entire lower half of `i` will be truncated (masked portion), plus the + // next `EXPONENT_BITS` bits. + let mask: I::UnsignedInt = (F::Int::MAX >> (F::Int::BITS / 2)).cast(); + (i_m >> F::EXPONENT_BITS | i_m & mask).cast() + } else { + // Generic case + + // Within the upper `F::BITS`, everything except for the signifcand + // gets truncated + let d1: F::Int = (i_m >> (I::BITS - F::BITS - F::SIGNIFICAND_BITS - 1)).cast(); + + // The entire rest of `i_m` gets truncated. Zero the upper `F::BITS` then just + // check if it is nonzero. + let d2: F::Int = (i_m << F::BITS >> F::BITS != I::UnsignedInt::ZERO).into(); + + d1 | d2 + }; + + // Branchlessly perform `if dropped_bits > 0 { 1 } else { 0 }` + let m_adj = (dropped_bits - (dropped_bits >> (F::BITS - 1) & !m_base)) >> (F::BITS - 1); + + // Add one when we need to round up. Break ties to even. + m_base + m_adj + }; + + // + not |, so the mantissa can overflow into the exponent. + let urepr = (e << F::SIGNIFICAND_BITS) + m; + + let repr: F::Int = if I::SIGNED { urepr | sign_bit } else { urepr }; + + F::from_repr(repr) } // Conversions from unsigned integers to floats. intrinsics! { #[arm_aeabi_alias = __aeabi_ui2f] pub extern "C" fn __floatunsisf(i: u32) -> f32 { - f32::from_bits(int_to_float::u32_to_f32_bits(i)) + int_to_float_generic(i) } #[arm_aeabi_alias = __aeabi_ui2d] pub extern "C" fn __floatunsidf(i: u32) -> f64 { - f64::from_bits(int_to_float::u32_to_f64_bits(i)) + int_to_float_generic(i) } #[arm_aeabi_alias = __aeabi_ul2f] pub extern "C" fn __floatundisf(i: u64) -> f32 { - f32::from_bits(int_to_float::u64_to_f32_bits(i)) + int_to_float_generic(i) } #[arm_aeabi_alias = __aeabi_ul2d] pub extern "C" fn __floatundidf(i: u64) -> f64 { - f64::from_bits(int_to_float::u64_to_f64_bits(i)) + int_to_float_generic(i) } #[cfg_attr(target_os = "uefi", unadjusted_on_win64)] pub extern "C" fn __floatuntisf(i: u128) -> f32 { - f32::from_bits(int_to_float::u128_to_f32_bits(i)) + int_to_float_generic(i) } #[cfg_attr(target_os = "uefi", unadjusted_on_win64)] pub extern "C" fn __floatuntidf(i: u128) -> f64 { - f64::from_bits(int_to_float::u128_to_f64_bits(i)) + int_to_float_generic(i) } } @@ -113,38 +128,32 @@ intrinsics! { intrinsics! { #[arm_aeabi_alias = __aeabi_i2f] pub extern "C" fn __floatsisf(i: i32) -> f32 { - let sign_bit = ((i >> 31) as u32) << 31; - f32::from_bits(int_to_float::u32_to_f32_bits(i.unsigned_abs()) | sign_bit) + int_to_float_generic(i) } #[arm_aeabi_alias = __aeabi_i2d] pub extern "C" fn __floatsidf(i: i32) -> f64 { - let sign_bit = ((i >> 31) as u64) << 63; - f64::from_bits(int_to_float::u32_to_f64_bits(i.unsigned_abs()) | sign_bit) + int_to_float_generic(i) } #[arm_aeabi_alias = __aeabi_l2f] pub extern "C" fn __floatdisf(i: i64) -> f32 { - let sign_bit = ((i >> 63) as u32) << 31; - f32::from_bits(int_to_float::u64_to_f32_bits(i.unsigned_abs()) | sign_bit) + int_to_float_generic(i) } #[arm_aeabi_alias = __aeabi_l2d] pub extern "C" fn __floatdidf(i: i64) -> f64 { - let sign_bit = ((i >> 63) as u64) << 63; - f64::from_bits(int_to_float::u64_to_f64_bits(i.unsigned_abs()) | sign_bit) + int_to_float_generic(i) } #[cfg_attr(target_os = "uefi", unadjusted_on_win64)] pub extern "C" fn __floattisf(i: i128) -> f32 { - let sign_bit = ((i >> 127) as u32) << 31; - f32::from_bits(int_to_float::u128_to_f32_bits(i.unsigned_abs()) | sign_bit) + int_to_float_generic(i) } #[cfg_attr(target_os = "uefi", unadjusted_on_win64)] pub extern "C" fn __floattidf(i: i128) -> f64 { - let sign_bit = ((i >> 127) as u64) << 63; - f64::from_bits(int_to_float::u128_to_f64_bits(i.unsigned_abs()) | sign_bit) + int_to_float_generic(i) } } diff --git a/src/float/mod.rs b/src/float/mod.rs index 5fef1df3..1125943a 100644 --- a/src/float/mod.rs +++ b/src/float/mod.rs @@ -31,10 +31,10 @@ pub(crate) trait Float: + ops::Rem { /// A uint of the same width as the float - type Int: Int; + type Int: Int; /// A int of the same width as the float - type SignedInt: Int; + type SignedInt: Int; /// An int capable of containing the exponent bits plus a sign bit. This is signed. type ExpInt: Int; diff --git a/src/int/mod.rs b/src/int/mod.rs index 45d38388..5949d319 100644 --- a/src/int/mod.rs +++ b/src/int/mod.rs @@ -82,6 +82,7 @@ pub(crate) trait Int: MinInt fn unsigned(self) -> Self::UnsignedInt; fn from_unsigned(unsigned: Self::UnsignedInt) -> Self; + fn unsigned_abs(self) -> Self::UnsignedInt; fn from_bool(b: bool) -> Self; @@ -177,7 +178,6 @@ macro_rules! int_impl_common { fn wrapping_mul(self, other: Self) -> Self { ::wrapping_mul(self, other) } - fn wrapping_sub(self, other: Self) -> Self { ::wrapping_sub(self, other) } @@ -234,6 +234,10 @@ macro_rules! int_impl { me } + fn unsigned_abs(self) -> Self { + self + } + fn abs_diff(self, other: Self) -> Self { if self < other { other.wrapping_sub(self) @@ -267,6 +271,10 @@ macro_rules! int_impl { me as $ity } + fn unsigned_abs(self) -> Self::UnsignedInt { + self.unsigned_abs() + } + fn abs_diff(self, other: Self) -> $uty { self.wrapping_sub(other).wrapping_abs() as $uty }