Skip to content

Commit

Permalink
Change integer to float conversions to be generic
Browse files Browse the repository at this point in the history
Reduce duplicate code and make it easier to add `f128` operations.
  • Loading branch information
tgross35 committed May 29, 2024
1 parent c04eb9e commit c185118
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 84 deletions.
171 changes: 90 additions & 81 deletions src/float/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,141 +10,150 @@ use super::Float;
/// which unfortunately isn't the easiest kind of code to read.
///
/// The algorithm is explained here: <https://blog.m-ou.se/floats/>
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, F>(i: I) -> F
where
F: Float,
I: Int<UnsignedInt: Int>,
I::UnsignedInt: CastInto<F::Int>,
F::Int: CastFrom<u32>,
F::Int: CastFrom<I>,
F::Int: From<bool>,
F::Int: CastInto<I::UnsignedInt>,
{
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)
}
}

// Conversions from signed integers to floats.
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)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/float/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ pub(crate) trait Float:
+ ops::Rem<Output = Self>
{
/// A uint of the same width as the float
type Int: Int;
type Int: Int<UnsignedInt = Self::Int, OtherSign = Self::SignedInt>;

/// A int of the same width as the float
type SignedInt: Int;
type SignedInt: Int<UnsignedInt = Self::Int, OtherSign = Self::Int>;

/// An int capable of containing the exponent bits plus a sign bit. This is signed.
type ExpInt: Int;
Expand Down
10 changes: 9 additions & 1 deletion src/int/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -177,7 +178,6 @@ macro_rules! int_impl_common {
fn wrapping_mul(self, other: Self) -> Self {
<Self>::wrapping_mul(self, other)
}

fn wrapping_sub(self, other: Self) -> Self {
<Self>::wrapping_sub(self, other)
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit c185118

Please sign in to comment.