Add __truncdfsf2 intrinsic
This adds the truncdfsf2 intrinsic and a corresponding fuzz test case. The implementation of trunc is generic to make it easy to add truncdfhs2 and truncsfhf2 if rust ever gets `f16` support.
This commit is contained in:
parent
c7dc3215a1
commit
9e65060184
4 changed files with 145 additions and 1 deletions
|
|
@ -183,7 +183,7 @@ features = ["c"]
|
|||
- [x] subdf3.c
|
||||
- [x] subsf3.c
|
||||
- [ ] truncdfhf2.c
|
||||
- [ ] truncdfsf2.c
|
||||
- [x] truncdfsf2.c
|
||||
- [ ] truncsfhf2.c
|
||||
- [x] udivdi3.c
|
||||
- [x] udivmoddi4.c
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ pub mod extend;
|
|||
pub mod mul;
|
||||
pub mod pow;
|
||||
pub mod sub;
|
||||
pub mod trunc;
|
||||
|
||||
public_test_dep! {
|
||||
/// Trait for some basic operations on floats
|
||||
|
|
|
|||
118
library/compiler-builtins/src/float/trunc.rs
Normal file
118
library/compiler-builtins/src/float/trunc.rs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
use float::Float;
|
||||
use int::{CastInto, Int};
|
||||
|
||||
fn trunc<F: Float, R: Float>(a: F) -> R
|
||||
where
|
||||
F::Int: CastInto<u64>,
|
||||
F::Int: CastInto<u32>,
|
||||
u64: CastInto<F::Int>,
|
||||
u32: CastInto<F::Int>,
|
||||
|
||||
R::Int: CastInto<u32>,
|
||||
u32: CastInto<R::Int>,
|
||||
F::Int: CastInto<R::Int>,
|
||||
{
|
||||
let src_zero = F::Int::ZERO;
|
||||
let src_one = F::Int::ONE;
|
||||
let src_bits = F::BITS;
|
||||
let src_exp_bias = F::EXPONENT_BIAS;
|
||||
|
||||
let src_min_normal = F::IMPLICIT_BIT;
|
||||
let src_significand_mask = F::SIGNIFICAND_MASK;
|
||||
let src_infinity = F::EXPONENT_MASK;
|
||||
let src_sign_mask = F::SIGN_MASK;
|
||||
let src_abs_mask = src_sign_mask - src_one;
|
||||
let round_mask = (src_one << (F::SIGNIFICAND_BITS - R::SIGNIFICAND_BITS)) - src_one;
|
||||
let halfway = src_one << (F::SIGNIFICAND_BITS - R::SIGNIFICAND_BITS - 1);
|
||||
let src_qnan = src_one << (F::SIGNIFICAND_BITS - 1);
|
||||
let src_nan_code = src_qnan - src_one;
|
||||
|
||||
let dst_zero = R::Int::ZERO;
|
||||
let dst_one = R::Int::ONE;
|
||||
let dst_bits = R::BITS;
|
||||
let dst_inf_exp = R::EXPONENT_MAX;
|
||||
let dst_exp_bias = R::EXPONENT_BIAS;
|
||||
|
||||
let underflow_exponent: F::Int = (src_exp_bias + 1 - dst_exp_bias).cast();
|
||||
let overflow_exponent: F::Int = (src_exp_bias + dst_inf_exp - dst_exp_bias).cast();
|
||||
let underflow: F::Int = underflow_exponent << F::SIGNIFICAND_BITS;
|
||||
let overflow: F::Int = overflow_exponent << F::SIGNIFICAND_BITS;
|
||||
|
||||
let dst_qnan = R::Int::ONE << (R::SIGNIFICAND_BITS - 1);
|
||||
let dst_nan_code = dst_qnan - dst_one;
|
||||
|
||||
let sign_bits_delta = F::SIGNIFICAND_BITS - R::SIGNIFICAND_BITS;
|
||||
// Break a into a sign and representation of the absolute value.
|
||||
let a_abs = a.repr() & src_abs_mask;
|
||||
let sign = a.repr() & src_sign_mask;
|
||||
let mut abs_result: R::Int;
|
||||
|
||||
if a_abs.wrapping_sub(underflow) < a_abs.wrapping_sub(overflow) {
|
||||
// The exponent of a is within the range of normal numbers in the
|
||||
// destination format. We can convert by simply right-shifting with
|
||||
// rounding and adjusting the exponent.
|
||||
abs_result = (a_abs >> sign_bits_delta).cast();
|
||||
let tmp = src_exp_bias.wrapping_sub(dst_exp_bias) << R::SIGNIFICAND_BITS;
|
||||
abs_result = abs_result.wrapping_sub(tmp.cast());
|
||||
|
||||
let round_bits = a_abs & round_mask;
|
||||
if round_bits > halfway {
|
||||
// Round to nearest.
|
||||
abs_result += dst_one;
|
||||
} else if round_bits == halfway {
|
||||
// Tie to even.
|
||||
abs_result += abs_result & dst_one;
|
||||
};
|
||||
} else if a_abs > src_infinity {
|
||||
// a is NaN.
|
||||
// Conjure the result by beginning with infinity, setting the qNaN
|
||||
// bit and inserting the (truncated) trailing NaN field.
|
||||
abs_result = (dst_inf_exp << R::SIGNIFICAND_BITS).cast();
|
||||
abs_result |= dst_qnan;
|
||||
abs_result |= dst_nan_code
|
||||
& ((a_abs & src_nan_code) >> (F::SIGNIFICAND_BITS - R::SIGNIFICAND_BITS)).cast();
|
||||
} else if a_abs >= overflow {
|
||||
// a overflows to infinity.
|
||||
abs_result = (dst_inf_exp << R::SIGNIFICAND_BITS).cast();
|
||||
} else {
|
||||
// a underflows on conversion to the destination type or is an exact
|
||||
// zero. The result may be a denormal or zero. Extract the exponent
|
||||
// to get the shift amount for the denormalization.
|
||||
let a_exp: u32 = (a_abs >> F::SIGNIFICAND_BITS).cast();
|
||||
let shift = src_exp_bias - dst_exp_bias - a_exp + 1;
|
||||
|
||||
let significand = (a.repr() & src_significand_mask) | src_min_normal;
|
||||
|
||||
// Right shift by the denormalization amount with sticky.
|
||||
if shift > F::SIGNIFICAND_BITS {
|
||||
abs_result = dst_zero;
|
||||
} else {
|
||||
let sticky = if (significand << (src_bits - shift)) != src_zero {
|
||||
src_one
|
||||
} else {
|
||||
src_zero
|
||||
};
|
||||
let denormalized_significand: F::Int = significand >> shift | sticky;
|
||||
abs_result =
|
||||
(denormalized_significand >> (F::SIGNIFICAND_BITS - R::SIGNIFICAND_BITS)).cast();
|
||||
let round_bits = denormalized_significand & round_mask;
|
||||
// Round to nearest
|
||||
if round_bits > halfway {
|
||||
abs_result += dst_one;
|
||||
}
|
||||
// Ties to even
|
||||
else if round_bits == halfway {
|
||||
abs_result += abs_result & dst_one;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the signbit to the absolute value.
|
||||
R::from_repr(abs_result | sign.wrapping_shr(src_bits - dst_bits).cast())
|
||||
}
|
||||
|
||||
intrinsics! {
|
||||
pub extern "C" fn __truncdfsf2(a: f64) -> f32 {
|
||||
trunc(a)
|
||||
}
|
||||
}
|
||||
|
|
@ -179,3 +179,28 @@ fn float_pow() {
|
|||
f64, 1e-12, __powidf2;
|
||||
);
|
||||
}
|
||||
|
||||
macro_rules! trunc {
|
||||
($fX:ident, $fD:ident, $fn:ident) => {
|
||||
fuzz_float(N, |x: $fX| {
|
||||
let tmp0 = x as $fD;
|
||||
let tmp1: $fD = $fn(x);
|
||||
if !Float::eq_repr(tmp0, tmp1) {
|
||||
panic!(
|
||||
"{}({}): std: {}, builtins: {}",
|
||||
stringify!($fn),
|
||||
x,
|
||||
tmp0,
|
||||
tmp1
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_trunc() {
|
||||
use compiler_builtins::float::trunc::__truncdfsf2;
|
||||
|
||||
trunc!(f64, f32, __truncdfsf2);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue