Merge pull request #613 from tgross35/f16-f128-convert
Add `f128` float to integer conversion functions
This commit is contained in:
commit
6ab5934b81
9 changed files with 261 additions and 187 deletions
|
|
@ -239,12 +239,12 @@ These builtins are needed to support `f16` and `f128`, which are in the process
|
|||
- [x] extendhfsf2.c
|
||||
- [x] extendhftf2.c
|
||||
- [x] extendsftf2.c
|
||||
- [ ] fixtfdi.c
|
||||
- [ ] fixtfsi.c
|
||||
- [ ] fixtfti.c
|
||||
- [ ] fixunstfdi.c
|
||||
- [ ] fixunstfsi.c
|
||||
- [ ] fixunstfti.c
|
||||
- [x] fixtfdi.c
|
||||
- [x] fixtfsi.c
|
||||
- [x] fixtfti.c
|
||||
- [x] fixunstfdi.c
|
||||
- [x] fixunstfsi.c
|
||||
- [x] fixunstfti.c
|
||||
- [ ] floatditf.c
|
||||
- [ ] floatsitf.c
|
||||
- [ ] floatunditf.c
|
||||
|
|
|
|||
|
|
@ -533,12 +533,6 @@ mod c {
|
|||
if (target_arch == "aarch64" || target_arch == "arm64ec") && consider_float_intrinsics {
|
||||
sources.extend(&[
|
||||
("__comparetf2", "comparetf2.c"),
|
||||
("__fixtfdi", "fixtfdi.c"),
|
||||
("__fixtfsi", "fixtfsi.c"),
|
||||
("__fixtfti", "fixtfti.c"),
|
||||
("__fixunstfdi", "fixunstfdi.c"),
|
||||
("__fixunstfsi", "fixunstfsi.c"),
|
||||
("__fixunstfti", "fixunstfti.c"),
|
||||
("__floatditf", "floatditf.c"),
|
||||
("__floatsitf", "floatsitf.c"),
|
||||
("__floatunditf", "floatunditf.c"),
|
||||
|
|
@ -561,9 +555,7 @@ mod c {
|
|||
if target_arch == "mips64" {
|
||||
sources.extend(&[
|
||||
("__netf2", "comparetf2.c"),
|
||||
("__fixtfsi", "fixtfsi.c"),
|
||||
("__floatsitf", "floatsitf.c"),
|
||||
("__fixunstfsi", "fixunstfsi.c"),
|
||||
("__floatunsitf", "floatunsitf.c"),
|
||||
("__fe_getround", "fp_mode.c"),
|
||||
]);
|
||||
|
|
@ -572,9 +564,7 @@ mod c {
|
|||
if target_arch == "loongarch64" {
|
||||
sources.extend(&[
|
||||
("__netf2", "comparetf2.c"),
|
||||
("__fixtfsi", "fixtfsi.c"),
|
||||
("__floatsitf", "floatsitf.c"),
|
||||
("__fixunstfsi", "fixunstfsi.c"),
|
||||
("__floatunsitf", "floatunsitf.c"),
|
||||
("__fe_getround", "fp_mode.c"),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
use core::ops::Neg;
|
||||
|
||||
use crate::int::{CastFrom, CastInto, Int, MinInt};
|
||||
|
||||
use super::Float;
|
||||
|
||||
/// Conversions from integers to floats.
|
||||
///
|
||||
/// These are hand-optimized bit twiddling code,
|
||||
|
|
@ -142,102 +148,133 @@ intrinsics! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Generic float to unsigned int conversions.
|
||||
fn float_to_unsigned_int<F, U>(f: F) -> U
|
||||
where
|
||||
F: Float,
|
||||
U: Int<UnsignedInt = U>,
|
||||
F::Int: CastInto<U>,
|
||||
F::Int: CastFrom<u32>,
|
||||
F::Int: CastInto<U::UnsignedInt>,
|
||||
u32: CastFrom<F::Int>,
|
||||
{
|
||||
float_to_int_inner::<F, U, _, _>(f.repr(), |i: U| i, || U::MAX)
|
||||
}
|
||||
|
||||
/// Generic float to signed int conversions.
|
||||
fn float_to_signed_int<F, I>(f: F) -> I
|
||||
where
|
||||
F: Float,
|
||||
I: Int + Neg<Output = I>,
|
||||
I::UnsignedInt: Int,
|
||||
F::Int: CastInto<I::UnsignedInt>,
|
||||
F::Int: CastFrom<u32>,
|
||||
u32: CastFrom<F::Int>,
|
||||
{
|
||||
float_to_int_inner::<F, I, _, _>(
|
||||
f.repr() & !F::SIGN_MASK,
|
||||
|i: I| if f.is_sign_negative() { -i } else { i },
|
||||
|| if f.is_sign_negative() { I::MIN } else { I::MAX },
|
||||
)
|
||||
}
|
||||
|
||||
/// Float to int conversions, generic for both signed and unsigned.
|
||||
///
|
||||
/// Parameters:
|
||||
/// - `fbits`: `abg(f)` bitcasted to an integer.
|
||||
/// - `map_inbounds`: apply this transformation to integers that are within range (add the sign
|
||||
/// back).
|
||||
/// - `out_of_bounds`: return value when out of range for `I`.
|
||||
fn float_to_int_inner<F, I, FnFoo, FnOob>(
|
||||
fbits: F::Int,
|
||||
map_inbounds: FnFoo,
|
||||
out_of_bounds: FnOob,
|
||||
) -> I
|
||||
where
|
||||
F: Float,
|
||||
I: Int,
|
||||
FnFoo: FnOnce(I) -> I,
|
||||
FnOob: FnOnce() -> I,
|
||||
I::UnsignedInt: Int,
|
||||
F::Int: CastInto<I::UnsignedInt>,
|
||||
F::Int: CastFrom<u32>,
|
||||
u32: CastFrom<F::Int>,
|
||||
{
|
||||
let int_max_exp = F::EXPONENT_BIAS + I::MAX.ilog2() + 1;
|
||||
let foobar = F::EXPONENT_BIAS + I::UnsignedInt::BITS - 1;
|
||||
|
||||
if fbits < F::ONE.repr() {
|
||||
// < 0 gets rounded to 0
|
||||
I::ZERO
|
||||
} else if fbits < F::Int::cast_from(int_max_exp) << F::SIGNIFICAND_BITS {
|
||||
// >= 1, < integer max
|
||||
let m_base = if I::UnsignedInt::BITS >= F::Int::BITS {
|
||||
I::UnsignedInt::cast_from(fbits) << (I::BITS - F::SIGNIFICAND_BITS - 1)
|
||||
} else {
|
||||
I::UnsignedInt::cast_from(fbits >> (F::SIGNIFICAND_BITS - I::BITS + 1))
|
||||
};
|
||||
|
||||
// Set the implicit 1-bit.
|
||||
let m: I::UnsignedInt = I::UnsignedInt::ONE << (I::BITS - 1) | m_base;
|
||||
|
||||
// Shift based on the exponent and bias.
|
||||
let s: u32 = (foobar) - u32::cast_from(fbits >> F::SIGNIFICAND_BITS);
|
||||
|
||||
let unsigned = m >> s;
|
||||
map_inbounds(I::from_unsigned(unsigned))
|
||||
} else if fbits <= F::EXPONENT_MASK {
|
||||
// >= max (incl. inf)
|
||||
out_of_bounds()
|
||||
} else {
|
||||
I::ZERO
|
||||
}
|
||||
}
|
||||
|
||||
// Conversions from floats to unsigned integers.
|
||||
intrinsics! {
|
||||
#[arm_aeabi_alias = __aeabi_f2uiz]
|
||||
pub extern "C" fn __fixunssfsi(f: f32) -> u32 {
|
||||
let fbits = f.to_bits();
|
||||
if fbits < 127 << 23 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 159 << 23 { // >= 1, < max
|
||||
let m = 1 << 31 | fbits << 8; // Mantissa and the implicit 1-bit.
|
||||
let s = 158 - (fbits >> 23); // Shift based on the exponent and bias.
|
||||
m >> s
|
||||
} else if fbits <= 255 << 23 { // >= max (incl. inf)
|
||||
u32::MAX
|
||||
} else { // Negative or NaN
|
||||
0
|
||||
}
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[arm_aeabi_alias = __aeabi_f2ulz]
|
||||
pub extern "C" fn __fixunssfdi(f: f32) -> u64 {
|
||||
let fbits = f.to_bits();
|
||||
if fbits < 127 << 23 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 191 << 23 { // >= 1, < max
|
||||
let m = 1 << 63 | (fbits as u64) << 40; // Mantissa and the implicit 1-bit.
|
||||
let s = 190 - (fbits >> 23); // Shift based on the exponent and bias.
|
||||
m >> s
|
||||
} else if fbits <= 255 << 23 { // >= max (incl. inf)
|
||||
u64::MAX
|
||||
} else { // Negative or NaN
|
||||
0
|
||||
}
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[win64_128bit_abi_hack]
|
||||
pub extern "C" fn __fixunssfti(f: f32) -> u128 {
|
||||
let fbits = f.to_bits();
|
||||
if fbits < 127 << 23 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 255 << 23 { // >= 1, < inf
|
||||
let m = 1 << 127 | (fbits as u128) << 104; // Mantissa and the implicit 1-bit.
|
||||
let s = 254 - (fbits >> 23); // Shift based on the exponent and bias.
|
||||
m >> s
|
||||
} else if fbits == 255 << 23 { // == inf
|
||||
u128::MAX
|
||||
} else { // Negative or NaN
|
||||
0
|
||||
}
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[arm_aeabi_alias = __aeabi_d2uiz]
|
||||
pub extern "C" fn __fixunsdfsi(f: f64) -> u32 {
|
||||
let fbits = f.to_bits();
|
||||
if fbits < 1023 << 52 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 1055 << 52 { // >= 1, < max
|
||||
let m = 1 << 31 | (fbits >> 21) as u32; // Mantissa and the implicit 1-bit.
|
||||
let s = 1054 - (fbits >> 52); // Shift based on the exponent and bias.
|
||||
m >> s
|
||||
} else if fbits <= 2047 << 52 { // >= max (incl. inf)
|
||||
u32::MAX
|
||||
} else { // Negative or NaN
|
||||
0
|
||||
}
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[arm_aeabi_alias = __aeabi_d2ulz]
|
||||
pub extern "C" fn __fixunsdfdi(f: f64) -> u64 {
|
||||
let fbits = f.to_bits();
|
||||
if fbits < 1023 << 52 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 1087 << 52 { // >= 1, < max
|
||||
let m = 1 << 63 | fbits << 11; // Mantissa and the implicit 1-bit.
|
||||
let s = 1086 - (fbits >> 52); // Shift based on the exponent and bias.
|
||||
m >> s
|
||||
} else if fbits <= 2047 << 52 { // >= max (incl. inf)
|
||||
u64::MAX
|
||||
} else { // Negative or NaN
|
||||
0
|
||||
}
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[win64_128bit_abi_hack]
|
||||
pub extern "C" fn __fixunsdfti(f: f64) -> u128 {
|
||||
let fbits = f.to_bits();
|
||||
if fbits < 1023 << 52 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 1151 << 52 { // >= 1, < max
|
||||
let m = 1 << 127 | (fbits as u128) << 75; // Mantissa and the implicit 1-bit.
|
||||
let s = 1150 - (fbits >> 52); // Shift based on the exponent and bias.
|
||||
m >> s
|
||||
} else if fbits <= 2047 << 52 { // >= max (incl. inf)
|
||||
u128::MAX
|
||||
} else { // Negative or NaN
|
||||
0
|
||||
}
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no-f16-f128"))]
|
||||
pub extern "C" fn __fixunstfsi(f: f128) -> u32 {
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no-f16-f128"))]
|
||||
pub extern "C" fn __fixunstfdi(f: f128) -> u64 {
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no-f16-f128"))]
|
||||
pub extern "C" fn __fixunstfti(f: f128) -> u128 {
|
||||
float_to_unsigned_int(f)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -245,103 +282,46 @@ intrinsics! {
|
|||
intrinsics! {
|
||||
#[arm_aeabi_alias = __aeabi_f2iz]
|
||||
pub extern "C" fn __fixsfsi(f: f32) -> i32 {
|
||||
let fbits = f.to_bits() & !0 >> 1; // Remove sign bit.
|
||||
if fbits < 127 << 23 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 158 << 23 { // >= 1, < max
|
||||
let m = 1 << 31 | fbits << 8; // Mantissa and the implicit 1-bit.
|
||||
let s = 158 - (fbits >> 23); // Shift based on the exponent and bias.
|
||||
let u = (m >> s) as i32; // Unsigned result.
|
||||
if f.is_sign_negative() { -u } else { u }
|
||||
} else if fbits <= 255 << 23 { // >= max (incl. inf)
|
||||
if f.is_sign_negative() { i32::MIN } else { i32::MAX }
|
||||
} else { // NaN
|
||||
0
|
||||
}
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[arm_aeabi_alias = __aeabi_f2lz]
|
||||
pub extern "C" fn __fixsfdi(f: f32) -> i64 {
|
||||
let fbits = f.to_bits() & !0 >> 1; // Remove sign bit.
|
||||
if fbits < 127 << 23 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 190 << 23 { // >= 1, < max
|
||||
let m = 1 << 63 | (fbits as u64) << 40; // Mantissa and the implicit 1-bit.
|
||||
let s = 190 - (fbits >> 23); // Shift based on the exponent and bias.
|
||||
let u = (m >> s) as i64; // Unsigned result.
|
||||
if f.is_sign_negative() { -u } else { u }
|
||||
} else if fbits <= 255 << 23 { // >= max (incl. inf)
|
||||
if f.is_sign_negative() { i64::MIN } else { i64::MAX }
|
||||
} else { // NaN
|
||||
0
|
||||
}
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[win64_128bit_abi_hack]
|
||||
pub extern "C" fn __fixsfti(f: f32) -> i128 {
|
||||
let fbits = f.to_bits() & !0 >> 1; // Remove sign bit.
|
||||
if fbits < 127 << 23 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 254 << 23 { // >= 1, < max
|
||||
let m = 1 << 127 | (fbits as u128) << 104; // Mantissa and the implicit 1-bit.
|
||||
let s = 254 - (fbits >> 23); // Shift based on the exponent and bias.
|
||||
let u = (m >> s) as i128; // Unsigned result.
|
||||
if f.is_sign_negative() { -u } else { u }
|
||||
} else if fbits <= 255 << 23 { // >= max (incl. inf)
|
||||
if f.is_sign_negative() { i128::MIN } else { i128::MAX }
|
||||
} else { // NaN
|
||||
0
|
||||
}
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[arm_aeabi_alias = __aeabi_d2iz]
|
||||
pub extern "C" fn __fixdfsi(f: f64) -> i32 {
|
||||
let fbits = f.to_bits() & !0 >> 1; // Remove sign bit.
|
||||
if fbits < 1023 << 52 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 1054 << 52 { // >= 1, < max
|
||||
let m = 1 << 31 | (fbits >> 21) as u32; // Mantissa and the implicit 1-bit.
|
||||
let s = 1054 - (fbits >> 52); // Shift based on the exponent and bias.
|
||||
let u = (m >> s) as i32; // Unsigned result.
|
||||
if f.is_sign_negative() { -u } else { u }
|
||||
} else if fbits <= 2047 << 52 { // >= max (incl. inf)
|
||||
if f.is_sign_negative() { i32::MIN } else { i32::MAX }
|
||||
} else { // NaN
|
||||
0
|
||||
}
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[arm_aeabi_alias = __aeabi_d2lz]
|
||||
pub extern "C" fn __fixdfdi(f: f64) -> i64 {
|
||||
let fbits = f.to_bits() & !0 >> 1; // Remove sign bit.
|
||||
if fbits < 1023 << 52 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 1086 << 52 { // >= 1, < max
|
||||
let m = 1 << 63 | fbits << 11; // Mantissa and the implicit 1-bit.
|
||||
let s = 1086 - (fbits >> 52); // Shift based on the exponent and bias.
|
||||
let u = (m >> s) as i64; // Unsigned result.
|
||||
if f.is_sign_negative() { -u } else { u }
|
||||
} else if fbits <= 2047 << 52 { // >= max (incl. inf)
|
||||
if f.is_sign_negative() { i64::MIN } else { i64::MAX }
|
||||
} else { // NaN
|
||||
0
|
||||
}
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[win64_128bit_abi_hack]
|
||||
pub extern "C" fn __fixdfti(f: f64) -> i128 {
|
||||
let fbits = f.to_bits() & !0 >> 1; // Remove sign bit.
|
||||
if fbits < 1023 << 52 { // >= 0, < 1
|
||||
0
|
||||
} else if fbits < 1150 << 52 { // >= 1, < max
|
||||
let m = 1 << 127 | (fbits as u128) << 75; // Mantissa and the implicit 1-bit.
|
||||
let s = 1150 - (fbits >> 52); // Shift based on the exponent and bias.
|
||||
let u = (m >> s) as i128; // Unsigned result.
|
||||
if f.is_sign_negative() { -u } else { u }
|
||||
} else if fbits <= 2047 << 52 { // >= max (incl. inf)
|
||||
if f.is_sign_negative() { i128::MIN } else { i128::MAX }
|
||||
} else { // NaN
|
||||
0
|
||||
}
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no-f16-f128"))]
|
||||
pub extern "C" fn __fixtfsi(f: f128) -> i32 {
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no-f16-f128"))]
|
||||
pub extern "C" fn __fixtfdi(f: f128) -> i64 {
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no-f16-f128"))]
|
||||
pub extern "C" fn __fixtfti(f: f128) -> i128 {
|
||||
float_to_signed_int(f)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ pub(crate) trait Float:
|
|||
/// compared.
|
||||
fn eq_repr(self, rhs: Self) -> bool;
|
||||
|
||||
/// Returns the sign bit
|
||||
fn sign(self) -> bool;
|
||||
/// Returns true if the sign is negative
|
||||
fn is_sign_negative(self) -> bool;
|
||||
|
||||
/// Returns the exponent with bias
|
||||
fn exp(self) -> Self::ExpInt;
|
||||
|
|
@ -150,8 +150,8 @@ macro_rules! float_impl {
|
|||
self.repr() == rhs.repr()
|
||||
}
|
||||
}
|
||||
fn sign(self) -> bool {
|
||||
self.signed_repr() < Self::SignedInt::ZERO
|
||||
fn is_sign_negative(self) -> bool {
|
||||
self.is_sign_negative()
|
||||
}
|
||||
fn exp(self) -> Self::ExpInt {
|
||||
((self.to_bits() & Self::EXPONENT_MASK) >> Self::SIGNIFICAND_BITS) as Self::ExpInt
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ pub(crate) trait Int: MinInt
|
|||
fn rotate_left(self, other: u32) -> Self;
|
||||
fn overflowing_add(self, other: Self) -> (Self, bool);
|
||||
fn leading_zeros(self) -> u32;
|
||||
fn ilog2(self) -> u32;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +201,10 @@ macro_rules! int_impl_common {
|
|||
fn leading_zeros(self) -> u32 {
|
||||
<Self>::leading_zeros(self)
|
||||
}
|
||||
|
||||
fn ilog2(self) -> u32 {
|
||||
<Self>::ilog2(self)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -380,6 +385,16 @@ public_test_dep! {
|
|||
pub(crate) trait CastInto<T: Copy>: Copy {
|
||||
fn cast(self) -> T;
|
||||
}
|
||||
|
||||
pub(crate) trait CastFrom<T: Copy>:Copy {
|
||||
fn cast_from(value: T) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, U: CastInto<T> + Copy> CastFrom<U> for T {
|
||||
fn cast_from(value: U) -> Self {
|
||||
value.cast()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cast_into {
|
||||
|
|
|
|||
|
|
@ -34,4 +34,6 @@ no-f16-f128 = ["compiler_builtins/no-f16-f128"]
|
|||
mem = ["compiler_builtins/mem"]
|
||||
mangled-names = ["compiler_builtins/mangled-names"]
|
||||
# Skip tests that rely on f128 symbols being available on the system
|
||||
no-sys-f128 = []
|
||||
no-sys-f128 = ["no-sys-f128-int-convert"]
|
||||
# Some platforms have some f128 functions but everything except integer conversions
|
||||
no-sys-f128-int-convert = []
|
||||
|
|
|
|||
|
|
@ -1,7 +1,15 @@
|
|||
use std::env;
|
||||
use std::{collections::HashSet, env};
|
||||
|
||||
/// Features to enable
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
enum Feature {
|
||||
NoSysF128,
|
||||
NoSysF128IntConvert,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let mut features = HashSet::new();
|
||||
|
||||
// These platforms do not have f128 symbols available in their system libraries, so
|
||||
// skip related tests.
|
||||
|
|
@ -21,7 +29,24 @@ fn main() {
|
|||
// <https://github.com/rust-lang/compiler-builtins/pull/606#issuecomment-2105657287>.
|
||||
|| target.starts_with("powerpc64-")
|
||||
{
|
||||
println!("cargo:warning=using apfloat fallback for f128");
|
||||
println!("cargo:rustc-cfg=feature=\"no-sys-f128\"");
|
||||
features.insert(Feature::NoSysF128);
|
||||
features.insert(Feature::NoSysF128IntConvert);
|
||||
}
|
||||
|
||||
if target.starts_with("i586") || target.starts_with("i686") {
|
||||
// 32-bit x86 seems to not have `__fixunstfti`, but does have everything else
|
||||
features.insert(Feature::NoSysF128IntConvert);
|
||||
}
|
||||
|
||||
for feature in features {
|
||||
let (name, warning) = match feature {
|
||||
Feature::NoSysF128 => ("no-sys-f128", "using apfloat fallback for f128"),
|
||||
Feature::NoSysF128IntConvert => (
|
||||
"no-sys-f128-int-convert",
|
||||
"using apfloat fallback for f128 to int conversions",
|
||||
),
|
||||
};
|
||||
println!("cargo:warning={warning}");
|
||||
println!("cargo:rustc-cfg=feature=\"{name}\"");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,7 +276,7 @@ macro_rules! apfloat_fallback {
|
|||
// The expression to run. This expression may use `FloatTy` for its signature.
|
||||
// Optionally, the final conversion back to a float can be suppressed using
|
||||
// `=> no_convert` (for e.g. operations that return a bool).
|
||||
$op:expr $(=> $convert:ident)?,
|
||||
$op:expr $(=> $convert:ident)? $(; $apfloat_op:expr)?,
|
||||
// Arguments that get passed to `$op` after converting to a float
|
||||
$($arg:expr),+
|
||||
$(,)?
|
||||
|
|
@ -292,26 +292,40 @@ macro_rules! apfloat_fallback {
|
|||
use rustc_apfloat::Float;
|
||||
type FloatTy = rustc_apfloat::ieee::$apfloat_ty;
|
||||
|
||||
let op_res = $op( $(FloatTy::from_bits($arg.to_bits().into())),+ );
|
||||
|
||||
apfloat_fallback!(@convert $float_ty, op_res $(,$convert)?)
|
||||
apfloat_fallback!(@inner
|
||||
fty: $float_ty,
|
||||
// Apply a conversion to `FloatTy` to each arg, then pass all args to `$op`
|
||||
op_res: $op( $(FloatTy::from_bits($arg.to_bits().into())),+ ),
|
||||
$(apfloat_op: $apfloat_op, )?
|
||||
$(conv_opts: $convert,)?
|
||||
args: $($arg),+
|
||||
)
|
||||
};
|
||||
|
||||
ret
|
||||
}};
|
||||
|
||||
// Operations that do not need converting back to a float
|
||||
(@convert $float_ty:ty, $val:expr, no_convert) => {
|
||||
(@inner fty: $float_ty:ty, op_res: $val:expr, conv_opts: no_convert, args: $($_arg:expr),+) => {
|
||||
$val
|
||||
};
|
||||
|
||||
// Some apfloat operations return a `StatusAnd` that we need to extract the value from. This
|
||||
// is the default.
|
||||
(@convert $float_ty:ty, $val:expr) => {{
|
||||
(@inner fty: $float_ty:ty, op_res: $val:expr, args: $($_arg:expr),+) => {{
|
||||
// ignore the status, just get the value
|
||||
let unwrapped = $val.value;
|
||||
|
||||
<$float_ty>::from_bits(FloatTy::to_bits(unwrapped).try_into().unwrap())
|
||||
}};
|
||||
|
||||
// This is the case where we can't use the same expression for the default builtin and
|
||||
// nonstandard apfloat fallbac (e.g. `as` casts in std are normal functions in apfloat, so
|
||||
// two separate expressions must be specified.
|
||||
(@inner
|
||||
fty: $float_ty:ty, op_res: $_val:expr,
|
||||
apfloat_op: $apfloat_op:expr, args: $($arg:expr),+
|
||||
) => {{
|
||||
$apfloat_op($($arg),+)
|
||||
}};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,14 +100,39 @@ mod f_to_i {
|
|||
use super::*;
|
||||
|
||||
macro_rules! f_to_i {
|
||||
($x:ident, $($f:ty, $fn:ident);*;) => {
|
||||
($x:ident, $f_ty:ty, $apfloat_ty:ident, $sys_available:meta, $($i_ty:ty, $fn:ident);*;) => {
|
||||
$(
|
||||
// it is undefined behavior in the first place to do conversions with NaNs
|
||||
if !$x.is_nan() {
|
||||
let conv0 = $x as $f;
|
||||
let conv1: $f = $fn($x);
|
||||
if !apfloat_fallback!(
|
||||
$f_ty, $apfloat_ty, $sys_available, |x: FloatTy| x.is_nan() => no_convert, $x
|
||||
) {
|
||||
let conv0 = apfloat_fallback!(
|
||||
$f_ty, $apfloat_ty, $sys_available,
|
||||
// Use an `as` cast when the builtin is available on the system.
|
||||
|x| x as $i_ty;
|
||||
// When the builtin is not available, we need to use a different conversion
|
||||
// method (since apfloat doesn't support `as` casting).
|
||||
|x: $f_ty| {
|
||||
use compiler_builtins::int::MinInt;
|
||||
|
||||
let apf = FloatTy::from_bits(x.to_bits().into());
|
||||
let bits: usize = <$i_ty>::BITS.try_into().unwrap();
|
||||
|
||||
let err_fn = || panic!(
|
||||
"Unable to convert value {x:?} to type {}:", stringify!($i_ty)
|
||||
);
|
||||
|
||||
if <$i_ty>::SIGNED {
|
||||
<$i_ty>::try_from(apf.to_i128(bits).value).ok().unwrap_or_else(err_fn)
|
||||
} else {
|
||||
<$i_ty>::try_from(apf.to_u128(bits).value).ok().unwrap_or_else(err_fn)
|
||||
}
|
||||
},
|
||||
$x
|
||||
);
|
||||
let conv1: $i_ty = $fn($x);
|
||||
if conv0 != conv1 {
|
||||
panic!("{}({}): std: {}, builtins: {}", stringify!($fn), $x, conv0, conv1);
|
||||
panic!("{}({:?}): std: {:?}, builtins: {:?}", stringify!($fn), $x, conv0, conv1);
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
|
@ -121,7 +146,7 @@ mod f_to_i {
|
|||
};
|
||||
|
||||
fuzz_float(N, |x: f32| {
|
||||
f_to_i!(x,
|
||||
f_to_i!(x, f32, Single, all(),
|
||||
u32, __fixunssfsi;
|
||||
u64, __fixunssfdi;
|
||||
u128, __fixunssfti;
|
||||
|
|
@ -139,7 +164,7 @@ mod f_to_i {
|
|||
};
|
||||
|
||||
fuzz_float(N, |x: f64| {
|
||||
f_to_i!(x,
|
||||
f_to_i!(x, f64, Double, all(),
|
||||
u32, __fixunsdfsi;
|
||||
u64, __fixunsdfdi;
|
||||
u128, __fixunsdfti;
|
||||
|
|
@ -149,6 +174,29 @@ mod f_to_i {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "no-f16-f128"))]
|
||||
fn f128_to_int() {
|
||||
use compiler_builtins::float::conv::{
|
||||
__fixtfdi, __fixtfsi, __fixtfti, __fixunstfdi, __fixunstfsi, __fixunstfti,
|
||||
};
|
||||
|
||||
fuzz_float(N, |x: f128| {
|
||||
f_to_i!(
|
||||
x,
|
||||
f128,
|
||||
Quad,
|
||||
not(feature = "no-sys-f128-int-convert"),
|
||||
u32, __fixunstfsi;
|
||||
u64, __fixunstfdi;
|
||||
u128, __fixunstfti;
|
||||
i32, __fixtfsi;
|
||||
i64, __fixtfdi;
|
||||
i128, __fixtfti;
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! conv {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue