refactor float conversion

This commit is contained in:
Aaron Kutch 2020-12-08 19:28:05 -06:00
parent 0ce47b3c1f
commit ec4fc5dab5
7 changed files with 191 additions and 215 deletions

View file

@ -1,90 +1,88 @@
use float::Float;
use int::Int;
use int::{CastInto, Int};
macro_rules! int_to_float {
($i:expr, $ity:ty, $fty:ty) => {{
let i = $i;
if i == 0 {
return 0.0;
}
fn int_to_float<I: Int, F: Float>(i: I) -> F
where
F::Int: CastInto<u32>,
F::Int: CastInto<I>,
I::UnsignedInt: CastInto<F::Int>,
u32: CastInto<F::Int>,
{
if i == I::ZERO {
return F::ZERO;
}
let mant_dig = <$fty>::SIGNIFICAND_BITS + 1;
let exponent_bias = <$fty>::EXPONENT_BIAS;
let two = I::UnsignedInt::ONE + I::UnsignedInt::ONE;
let four = two + two;
let sign = i < I::ZERO;
let mut x = Int::abs_diff(i, I::ZERO);
let n = <$ity as Int>::BITS;
let (s, a) = i.extract_sign();
let mut a = a;
// number of significant digits in the integer
let i_sd = I::BITS - x.leading_zeros();
// significant digits for the float, including implicit bit
let f_sd = F::SIGNIFICAND_BITS + 1;
// number of significant digits
let sd = n - a.leading_zeros();
// exponent
let mut exp = i_sd - 1;
// exponent
let mut e = sd - 1;
if I::BITS < f_sd {
return F::from_parts(
sign,
(exp + F::EXPONENT_BIAS).cast(),
x.cast() << (f_sd - exp - 1),
);
}
if <$ity as Int>::BITS < mant_dig {
return <$fty>::from_parts(
s,
(e + exponent_bias) as <$fty as Float>::Int,
(a as <$fty as Float>::Int) << (mant_dig - e - 1),
);
}
a = if sd > mant_dig {
/* start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
* finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
* 12345678901234567890123456
* 1 = msb 1 bit
* P = bit MANT_DIG-1 bits to the right of 1
* Q = bit MANT_DIG bits to the right of 1
* R = "or" of all bits to the right of Q
*/
let mant_dig_plus_one = mant_dig + 1;
let mant_dig_plus_two = mant_dig + 2;
a = if sd == mant_dig_plus_one {
a << 1
} else if sd == mant_dig_plus_two {
a
} else {
(a >> (sd - mant_dig_plus_two)) as <$ity as Int>::UnsignedInt
| ((a & <$ity as Int>::UnsignedInt::max_value())
.wrapping_shl((n + mant_dig_plus_two) - sd)
!= 0) as <$ity as Int>::UnsignedInt
};
/* finish: */
a |= ((a & 4) != 0) as <$ity as Int>::UnsignedInt; /* Or P into R */
a += 1; /* round - this step may add a significant bit */
a >>= 2; /* dump Q and R */
/* a is now rounded to mant_dig or mant_dig+1 bits */
if (a & (1 << mant_dig)) != 0 {
a >>= 1;
e += 1;
}
a
/* a is now rounded to mant_dig bits */
x = if i_sd > f_sd {
// start: 0000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQxxxxxxxxxxxxxxxxxx
// finish: 000000000000000000000000000000000000001xxxxxxxxxxxxxxxxxxxxxxPQR
// 12345678901234567890123456
// 1 = the implicit bit
// P = bit f_sd-1 bits to the right of 1
// Q = bit f_sd bits to the right of 1
// R = "or" of all bits to the right of Q
let f_sd_add2 = f_sd + 2;
x = if i_sd == (f_sd + 1) {
x << 1
} else if i_sd == f_sd_add2 {
x
} else {
a.wrapping_shl(mant_dig - sd)
/* a is now rounded to mant_dig bits */
(x >> (i_sd - f_sd_add2))
| Int::from_bool(
(x & I::UnsignedInt::MAX).wrapping_shl((I::BITS + f_sd_add2) - i_sd)
!= Int::ZERO,
)
};
<$fty>::from_parts(
s,
(e + exponent_bias) as <$fty as Float>::Int,
a as <$fty as Float>::Int,
)
}};
// R |= P
x |= Int::from_bool((x & four) != I::UnsignedInt::ZERO);
// round - this step may add a significant bit
x += Int::ONE;
// dump Q and R
x >>= 2;
// a is now rounded to f_sd or f_sd+1 bits
if (x & (I::UnsignedInt::ONE << f_sd)) != Int::ZERO {
x >>= 1;
exp += 1;
}
x
} else {
x.wrapping_shl(f_sd - i_sd)
};
F::from_parts(sign, (exp + F::EXPONENT_BIAS).cast(), x.cast())
}
intrinsics! {
#[arm_aeabi_alias = __aeabi_i2f]
pub extern "C" fn __floatsisf(i: i32) -> f32 {
int_to_float!(i, i32, f32)
int_to_float(i)
}
#[arm_aeabi_alias = __aeabi_i2d]
pub extern "C" fn __floatsidf(i: i32) -> f64 {
int_to_float!(i, i32, f64)
int_to_float(i)
}
#[maybe_use_optimized_c_shim]
@ -95,7 +93,7 @@ intrinsics! {
if cfg!(target_arch = "x86_64") {
i as f32
} else {
int_to_float!(i, i64, f32)
int_to_float(i)
}
}
@ -107,181 +105,172 @@ intrinsics! {
if cfg!(target_arch = "x86_64") {
i as f64
} else {
int_to_float!(i, i64, f64)
int_to_float(i)
}
}
#[unadjusted_on_win64]
pub extern "C" fn __floattisf(i: i128) -> f32 {
int_to_float!(i, i128, f32)
int_to_float(i)
}
#[unadjusted_on_win64]
pub extern "C" fn __floattidf(i: i128) -> f64 {
int_to_float!(i, i128, f64)
int_to_float(i)
}
#[arm_aeabi_alias = __aeabi_ui2f]
pub extern "C" fn __floatunsisf(i: u32) -> f32 {
int_to_float!(i, u32, f32)
int_to_float(i)
}
#[arm_aeabi_alias = __aeabi_ui2d]
pub extern "C" fn __floatunsidf(i: u32) -> f64 {
int_to_float!(i, u32, f64)
int_to_float(i)
}
#[maybe_use_optimized_c_shim]
#[arm_aeabi_alias = __aeabi_ul2f]
pub extern "C" fn __floatundisf(i: u64) -> f32 {
int_to_float!(i, u64, f32)
int_to_float(i)
}
#[maybe_use_optimized_c_shim]
#[arm_aeabi_alias = __aeabi_ul2d]
pub extern "C" fn __floatundidf(i: u64) -> f64 {
int_to_float!(i, u64, f64)
int_to_float(i)
}
#[unadjusted_on_win64]
pub extern "C" fn __floatuntisf(i: u128) -> f32 {
int_to_float!(i, u128, f32)
int_to_float(i)
}
#[unadjusted_on_win64]
pub extern "C" fn __floatuntidf(i: u128) -> f64 {
int_to_float!(i, u128, f64)
int_to_float(i)
}
}
#[derive(PartialEq)]
enum Sign {
Positive,
Negative,
}
fn float_to_int<F: Float, I: Int>(f: F) -> I
where
F::ExpInt: CastInto<u32>,
u32: CastInto<F::ExpInt>,
F::Int: CastInto<I>,
{
// converting NaNs is UB, so we don't consider them
macro_rules! float_to_int {
($f:expr, $fty:ty, $ity:ty) => {{
let f = $f;
let fixint_min = <$ity>::min_value();
let fixint_max = <$ity>::max_value();
let fixint_bits = <$ity as Int>::BITS as usize;
let fixint_unsigned = fixint_min == 0;
let sign = f.sign();
let mut exp = f.exp();
let sign_bit = <$fty>::SIGN_MASK;
let significand_bits = <$fty>::SIGNIFICAND_BITS as usize;
let exponent_bias = <$fty>::EXPONENT_BIAS as usize;
//let exponent_max = <$fty>::exponent_max() as usize;
// if less than one or unsigned & negative
if (exp < F::EXPONENT_BIAS.cast()) || (!I::SIGNED && sign) {
return I::ZERO;
}
exp -= F::EXPONENT_BIAS.cast();
// Break a into sign, exponent, significand
let a_rep = <$fty>::repr(f);
let a_abs = a_rep & !sign_bit;
// this is used to work around -1 not being available for unsigned
let sign = if (a_rep & sign_bit) == 0 {
Sign::Positive
// If the value is too large for `I`, saturate.
let bits: F::ExpInt = I::BITS.cast();
let max = if I::SIGNED {
bits - F::ExpInt::ONE
} else {
bits
};
if max <= exp {
return if sign {
// It happens that I::MIN is handled correctly
I::MIN
} else {
Sign::Negative
I::MAX
};
let mut exponent = (a_abs >> significand_bits) as usize;
let significand = (a_abs & <$fty>::SIGNIFICAND_MASK) | <$fty>::IMPLICIT_BIT;
};
// if < 1 or unsigned & negative
if exponent < exponent_bias || fixint_unsigned && sign == Sign::Negative {
return 0;
}
exponent -= exponent_bias;
// `0 <= exp < max`
// If the value is infinity, saturate.
// If the value is too large for the integer type, 0.
if exponent
>= (if fixint_unsigned {
fixint_bits
} else {
fixint_bits - 1
})
{
return if sign == Sign::Positive {
fixint_max
} else {
fixint_min
};
}
// If 0 <= exponent < significand_bits, right shift to get the result.
// Otherwise, shift left.
// (sign - 1) will never overflow as negative signs are already returned as 0 for unsigned
let r = if exponent < significand_bits {
(significand >> (significand_bits - exponent)) as $ity
// If 0 <= exponent < F::SIGNIFICAND_BITS, right shift to get the result. Otherwise, shift left.
let sig_bits: F::ExpInt = F::SIGNIFICAND_BITS.cast();
// The larger integer has to be casted into, or else the shift overflows
let r: I = if F::Int::BITS < I::BITS {
let tmp: I = if exp < sig_bits {
f.imp_frac().cast() >> (sig_bits - exp).cast()
} else {
(significand as $ity) << (exponent - significand_bits)
f.imp_frac().cast() << (exp - sig_bits).cast()
};
if sign == Sign::Negative {
(!r).wrapping_add(1)
tmp
} else {
let tmp: F::Int = if exp < sig_bits {
f.imp_frac() >> (sig_bits - exp).cast()
} else {
r
}
}};
f.imp_frac() << (exp - sig_bits).cast()
};
tmp.cast()
};
if sign {
r.wrapping_neg()
} else {
r
}
}
intrinsics! {
#[arm_aeabi_alias = __aeabi_f2iz]
pub extern "C" fn __fixsfsi(f: f32) -> i32 {
float_to_int!(f, f32, i32)
float_to_int(f)
}
#[arm_aeabi_alias = __aeabi_f2lz]
pub extern "C" fn __fixsfdi(f: f32) -> i64 {
float_to_int!(f, f32, i64)
float_to_int(f)
}
#[unadjusted_on_win64]
pub extern "C" fn __fixsfti(f: f32) -> i128 {
float_to_int!(f, f32, i128)
float_to_int(f)
}
#[arm_aeabi_alias = __aeabi_d2iz]
pub extern "C" fn __fixdfsi(f: f64) -> i32 {
float_to_int!(f, f64, i32)
float_to_int(f)
}
#[arm_aeabi_alias = __aeabi_d2lz]
pub extern "C" fn __fixdfdi(f: f64) -> i64 {
float_to_int!(f, f64, i64)
float_to_int(f)
}
#[unadjusted_on_win64]
pub extern "C" fn __fixdfti(f: f64) -> i128 {
float_to_int!(f, f64, i128)
float_to_int(f)
}
#[arm_aeabi_alias = __aeabi_f2uiz]
pub extern "C" fn __fixunssfsi(f: f32) -> u32 {
float_to_int!(f, f32, u32)
float_to_int(f)
}
#[arm_aeabi_alias = __aeabi_f2ulz]
pub extern "C" fn __fixunssfdi(f: f32) -> u64 {
float_to_int!(f, f32, u64)
float_to_int(f)
}
#[unadjusted_on_win64]
pub extern "C" fn __fixunssfti(f: f32) -> u128 {
float_to_int!(f, f32, u128)
float_to_int(f)
}
#[arm_aeabi_alias = __aeabi_d2uiz]
pub extern "C" fn __fixunsdfsi(f: f64) -> u32 {
float_to_int!(f, f64, u32)
float_to_int(f)
}
#[arm_aeabi_alias = __aeabi_d2ulz]
pub extern "C" fn __fixunsdfdi(f: f64) -> u64 {
float_to_int!(f, f64, u64)
float_to_int(f)
}
#[unadjusted_on_win64]
pub extern "C" fn __fixunsdfti(f: f64) -> u128 {
float_to_int!(f, f64, u128)
float_to_int(f)
}
}

View file

@ -30,6 +30,9 @@ pub trait Float:
/// A int of the same with as the float
type SignedInt: Int;
/// An int capable of containing the exponent bits plus a sign bit. This is signed.
type ExpInt: Int;
const ZERO: Self;
const ONE: Self;
@ -71,6 +74,18 @@ pub trait Float:
/// compared.
fn eq_repr(self, rhs: Self) -> bool;
/// Returns the sign bit
fn sign(self) -> bool;
/// Returns the exponent with bias
fn exp(self) -> Self::ExpInt;
/// Returns the significand with no implicit bit (or the "fractional" part)
fn frac(self) -> Self::Int;
/// Returns the significand with implicit bit
fn imp_frac(self) -> Self::Int;
/// Returns a `Self::Int` transmuted back to `Self`
fn from_repr(a: Self::Int) -> Self;
@ -81,14 +96,16 @@ pub trait Float:
fn normalize(significand: Self::Int) -> (i32, Self::Int);
/// Returns if `self` is subnormal
fn is_subnormal(&self) -> bool;
fn is_subnormal(self) -> bool;
}
macro_rules! float_impl {
($ty:ident, $ity:ident, $sity:ident, $bits:expr, $significand_bits:expr) => {
($ty:ident, $ity:ident, $sity:ident, $expty:ident, $bits:expr, $significand_bits:expr) => {
impl Float for $ty {
type Int = $ity;
type SignedInt = $sity;
type ExpInt = $expty;
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
@ -113,6 +130,18 @@ macro_rules! float_impl {
self.repr() == rhs.repr()
}
}
fn sign(self) -> bool {
self.signed_repr() < Self::SignedInt::ZERO
}
fn exp(self) -> Self::ExpInt {
((self.to_bits() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS) as Self::ExpInt
}
fn frac(self) -> Self::Int {
self.to_bits() & Self::SIGNIFICAND_MASK
}
fn imp_frac(self) -> Self::Int {
self.frac() | Self::IMPLICIT_BIT
}
fn from_repr(a: Self::Int) -> Self {
Self::from_bits(a)
}
@ -132,12 +161,12 @@ macro_rules! float_impl {
significand << shift as Self::Int,
)
}
fn is_subnormal(&self) -> bool {
fn is_subnormal(self) -> bool {
(self.repr() & Self::EXPONENT_MASK) == Self::Int::ZERO
}
}
};
}
float_impl!(f32, u32, i32, 32, 23);
float_impl!(f64, u64, i64, 64, 52);
float_impl!(f32, u32, i32, i16, 32, 23);
float_impl!(f64, u64, i64, i16, 64, 52);

View file

@ -1,5 +1,4 @@
use float::Float;
use int::Int;
trait Pow: Float {
/// Returns `a` raised to the power `b`
@ -11,7 +10,7 @@ trait Pow: Float {
if (b & 1) != 0 {
r *= a;
}
b = b.aborting_div(2);
b = ((b as u32) >> 1) as i32;
if b == 0 {
break;
}

View file

@ -15,9 +15,11 @@ pub use self::leading_zeros::__clzsi2;
#[doc(hidden)]
pub trait Int:
Copy
+ core::fmt::Debug
+ PartialEq
+ PartialOrd
+ ops::AddAssign
+ ops::SubAssign
+ ops::BitAndAssign
+ ops::BitOrAssign
+ ops::BitXorAssign
@ -38,12 +40,16 @@ pub trait Int:
/// Unsigned version of Self
type UnsignedInt: Int;
/// If `Self` is a signed integer
const SIGNED: bool;
/// The bitwidth of the int type
const BITS: u32;
const ZERO: Self;
const ONE: Self;
const MIN: Self;
const MAX: Self;
/// LUT used for maximizing the space covered and minimizing the computational cost of fuzzing
/// in `testcrate`. For example, Self = u128 produces [0,1,2,7,8,15,16,31,32,63,64,95,96,111,
@ -52,18 +58,6 @@ pub trait Int:
/// The number of entries of `FUZZ_LENGTHS` actually used. The maximum is 20 for u128.
const FUZZ_NUM: usize;
/// Extracts the sign from self and returns a tuple.
///
/// # Examples
///
/// ```rust,ignore
/// let i = -25_i32;
/// let (sign, u) = i.extract_sign();
/// assert_eq!(sign, true);
/// assert_eq!(u, 25_u32);
/// ```
fn extract_sign(self) -> (bool, Self::UnsignedInt);
fn unsigned(self) -> Self::UnsignedInt;
fn from_unsigned(unsigned: Self::UnsignedInt) -> Self;
@ -77,8 +71,6 @@ pub trait Int:
// copied from primitive integers, but put in a trait
fn is_zero(self) -> bool;
fn max_value() -> Self;
fn min_value() -> Self;
fn wrapping_neg(self) -> Self;
fn wrapping_add(self, other: Self) -> Self;
fn wrapping_mul(self, other: Self) -> Self;
@ -87,25 +79,18 @@ pub trait Int:
fn wrapping_shr(self, other: u32) -> Self;
fn rotate_left(self, other: u32) -> Self;
fn overflowing_add(self, other: Self) -> (Self, bool);
fn aborting_div(self, other: Self) -> Self;
fn aborting_rem(self, other: Self) -> Self;
fn leading_zeros(self) -> u32;
}
fn unwrap<T>(t: Option<T>) -> T {
match t {
Some(t) => t,
None => ::abort(),
}
}
macro_rules! int_impl_common {
($ty:ty) => {
const BITS: u32 = <Self>::BITS;
const SIGNED: bool = Self::MIN != Self::ZERO;
const ZERO: Self = 0;
const ONE: Self = 1;
const MIN: Self = <Self>::MIN;
const MAX: Self = <Self>::MAX;
const FUZZ_LENGTHS: [u8; 20] = {
let bits = <Self as Int>::BITS;
@ -177,14 +162,6 @@ macro_rules! int_impl_common {
self == Self::ZERO
}
fn max_value() -> Self {
<Self>::max_value()
}
fn min_value() -> Self {
<Self>::min_value()
}
fn wrapping_neg(self) -> Self {
<Self>::wrapping_neg(self)
}
@ -217,14 +194,6 @@ macro_rules! int_impl_common {
<Self>::overflowing_add(self, other)
}
fn aborting_div(self, other: Self) -> Self {
unwrap(<Self>::checked_div(self, other))
}
fn aborting_rem(self, other: Self) -> Self {
unwrap(<Self>::checked_rem(self, other))
}
fn leading_zeros(self) -> u32 {
<Self>::leading_zeros(self)
}
@ -237,10 +206,6 @@ macro_rules! int_impl {
type OtherSign = $ity;
type UnsignedInt = $uty;
fn extract_sign(self) -> (bool, $uty) {
(false, self)
}
fn unsigned(self) -> $uty {
self
}
@ -264,14 +229,6 @@ macro_rules! int_impl {
type OtherSign = $uty;
type UnsignedInt = $uty;
fn extract_sign(self) -> (bool, $uty) {
if self < 0 {
(true, (!(self as $uty)).wrapping_add(1))
} else {
(false, self as $uty)
}
}
fn unsigned(self) -> $uty {
self as $uty
}
@ -395,13 +352,14 @@ impl_h_int!(
);
/// Trait to express (possibly lossy) casting of integers
pub(crate) trait CastInto<T: Copy>: Copy {
#[doc(hidden)]
pub trait CastInto<T: Copy>: Copy {
fn cast(self) -> T;
}
macro_rules! cast_into {
($ty:ty) => {
cast_into!($ty; usize, isize, u32, i32, u64, i64, u128, i128);
cast_into!($ty; usize, isize, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128);
};
($ty:ty; $($into:ty),*) => {$(
impl CastInto<$into> for $ty {
@ -412,6 +370,12 @@ macro_rules! cast_into {
)*};
}
cast_into!(usize);
cast_into!(isize);
cast_into!(u8);
cast_into!(i8);
cast_into!(u16);
cast_into!(i16);
cast_into!(u32);
cast_into!(i32);
cast_into!(u64);

View file

@ -31,11 +31,6 @@
#[cfg(test)]
extern crate core;
#[allow(unused_unsafe)]
fn abort() -> ! {
unsafe { core::intrinsics::abort() }
}
#[macro_use]
mod macros;

View file

@ -108,7 +108,7 @@ macro_rules! float {
let quo0 = x / y;
let quo1: $i = $fn(x, y);
// division of subnormals is not currently handled
if !(Float::is_subnormal(&quo0) || Float::is_subnormal(&quo1)) {
if !(Float::is_subnormal(quo0) || Float::is_subnormal(quo1)) {
if !Float::eq_repr(quo0, quo1) {
panic!(
"{}({}, {}): std: {}, builtins: {}",

View file

@ -86,7 +86,7 @@ macro_rules! float_mul {
let mul0 = x * y;
let mul1: $f = $fn(x, y);
// multiplication of subnormals is not currently handled
if !(Float::is_subnormal(&mul0) || Float::is_subnormal(&mul1)) {
if !(Float::is_subnormal(mul0) || Float::is_subnormal(mul1)) {
if !Float::eq_repr(mul0, mul1) {
panic!(
"{}({}, {}): std: {}, builtins: {}",