浏览代码

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.
Ayrton 3 年之前
父节点
当前提交
67c763f4e9
共有 4 个文件被更改,包括 145 次插入1 次删除
  1. 1 1
      README.md
  2. 1 0
      src/float/mod.rs
  3. 118 0
      src/float/trunc.rs
  4. 25 0
      testcrate/tests/misc.rs

+ 1 - 1
README.md

@@ -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

+ 1 - 0
src/float/mod.rs

@@ -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 - 0
src/float/trunc.rs

@@ -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)
+    }
+}

+ 25 - 0
testcrate/tests/misc.rs

@@ -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);
+}