浏览代码

comparesf2/comparedf2: fix a signedness bug and add tests.

whitequark 7 年之前
父节点
当前提交
07b446a9ab
共有 7 个文件被更改,包括 376 次插入14 次删除
  1. 318 0
      build.rs
  2. 17 11
      src/float/cmp.rs
  3. 13 3
      src/float/mod.rs
  4. 7 0
      tests/gedf2.rs
  5. 7 0
      tests/gesf2.rs
  6. 7 0
      tests/ledf2.rs
  7. 7 0
      tests/lesf2.rs

+ 318 - 0
build.rs

@@ -89,6 +89,12 @@ mod tests {
             Adddf3,
             Addsf3,
 
+            // float/cmp.rs
+            Gedf2,
+            Gesf2,
+            Ledf2,
+            Lesf2,
+
             // float/conv.rs
             Fixdfdi,
             Fixdfsi,
@@ -2529,6 +2535,318 @@ fn floatuntidf() {
         }
     }
 
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Gedf2 {
+        a: u64,
+        b: u64,
+        c: i32,
+    }
+
+    impl TestCase for Gedf2 {
+        fn name() -> &'static str {
+            "gedf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f64(rng);
+            let b = gen_f64(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = -1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Gedf2 { a: to_u64(a), b: to_u64(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__gedf2;
+
+fn to_f64(x: u64) -> f64 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u64, u64), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn gedf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __gedf2(to_f64(a), to_f64(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Gesf2 {
+        a: u32,
+        b: u32,
+        c: i32,
+    }
+
+    impl TestCase for Gesf2 {
+        fn name() -> &'static str {
+            "gesf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f32(rng);
+            let b = gen_f32(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = -1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Gesf2 { a: to_u32(a), b: to_u32(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__gesf2;
+
+fn to_f32(x: u32) -> f32 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u32, u32), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn gesf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __gesf2(to_f32(a), to_f32(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Ledf2 {
+        a: u64,
+        b: u64,
+        c: i32,
+    }
+
+    impl TestCase for Ledf2 {
+        fn name() -> &'static str {
+            "ledf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f64(rng);
+            let b = gen_f64(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = 1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Ledf2 { a: to_u64(a), b: to_u64(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__ledf2;
+
+fn to_f64(x: u64) -> f64 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u64, u64), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn ledf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __ledf2(to_f64(a), to_f64(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
+    #[derive(Eq, Hash, PartialEq)]
+    pub struct Lesf2 {
+        a: u32,
+        b: u32,
+        c: i32,
+    }
+
+    impl TestCase for Lesf2 {
+        fn name() -> &'static str {
+            "lesf2"
+        }
+
+        fn generate<R>(rng: &mut R) -> Option<Self>
+        where
+            R: Rng,
+            Self: Sized,
+        {
+            let a = gen_f32(rng);
+            let b = gen_f32(rng);
+            // TODO accept NaNs. We don't do that right now because we can't check
+            // for NaN-ness on the thumb targets (due to missing intrinsics)
+            if a.is_nan() || b.is_nan() {
+                return None;
+            }
+
+            let c;
+            if a.is_nan() || b.is_nan() {
+                c = 1;
+            } else if a < b {
+                c = -1;
+            } else if a > b {
+                c = 1;
+            } else {
+                c = 0;
+            }
+
+            Some(Lesf2 { a: to_u32(a), b: to_u32(b), c })
+        }
+
+        fn to_string(&self, buffer: &mut String) {
+            writeln!(
+                buffer,
+                "(({a}, {b}), {c}),",
+                a = self.a,
+                b = self.b,
+                c = self.c
+            )
+                    .unwrap();
+        }
+
+        fn prologue() -> &'static str {
+            "
+use std::mem;
+use compiler_builtins::float::cmp::__lesf2;
+
+fn to_f32(x: u32) -> f32 {
+    unsafe { mem::transmute(x) }
+}
+
+static TEST_CASES: &[((u32, u32), i32)] = &[
+"
+        }
+
+        fn epilogue() -> &'static str {
+            "
+];
+
+#[test]
+fn lesf2() {
+    for &((a, b), c) in TEST_CASES {
+        let c_ = __lesf2(to_f32(a), to_f32(b));
+        assert_eq!(((a, b), c), ((a, b), c_));
+    }
+}
+"
+        }
+    }
+
     #[derive(Eq, Hash, PartialEq)]
     pub struct Moddi3 {
         a: i64,

+ 17 - 11
src/float/cmp.rs

@@ -1,3 +1,5 @@
+#![allow(unreachable_code)]
+
 use int::{Int, CastInto};
 use float::Float;
 
@@ -35,18 +37,19 @@ fn cmp<F: Float>(a: F, b: F) -> Result where
     i32: CastInto<F::Int>,
     F::Int: CastInto<i32>,
 {
-    let one = F::Int::ONE;
-    let zero = F::Int::ZERO;
+    let one   = F::Int::ONE;
+    let zero  = F::Int::ZERO;
+    let szero = F::SignedInt::ZERO;
 
     let sign_bit =      F::SIGN_MASK as F::Int;
     let abs_mask =      sign_bit - one;
     let exponent_mask = F::EXPONENT_MASK;
     let inf_rep =       exponent_mask;
 
-    let a_rep = a.repr();
-    let b_rep = b.repr();
-    let a_abs = a_rep & abs_mask;
-    let b_abs = b_rep & abs_mask;
+    let a_rep  = a.repr();
+    let b_rep  = b.repr();
+    let a_abs  = a_rep & abs_mask;
+    let b_abs  = b_rep & abs_mask;
 
     // If either a or b is NaN, they are unordered.
     if a_abs > inf_rep || b_abs > inf_rep {
@@ -58,12 +61,15 @@ fn cmp<F: Float>(a: F, b: F) -> Result where
         return Result::Equal
     }
 
+    let a_srep = a.signed_repr();
+    let b_srep = b.signed_repr();
+
     // If at least one of a and b is positive, we get the same result comparing
     // a and b as signed integers as we would with a fp_ting-point compare.
-    if a_rep & b_rep >= zero {
-        if a_rep < b_rep {
+    if a_srep & b_srep >= szero {
+        if a_srep < b_srep {
             return Result::Less
-        } else if a_rep == b_rep {
+        } else if a_srep == b_srep {
             return Result::Equal
         } else {
             return Result::Greater
@@ -75,9 +81,9 @@ fn cmp<F: Float>(a: F, b: F) -> Result where
     // complement integer representation; if integers are represented in a
     // sign-magnitude representation, then this flip is incorrect).
     else {
-        if a_rep > b_rep {
+        if a_srep > b_srep {
             return Result::Less
-        } else if a_rep == b_rep {
+        } else if a_srep == b_srep {
             return Result::Equal
         } else {
             return Result::Greater

+ 13 - 3
src/float/mod.rs

@@ -26,6 +26,9 @@ pub trait Float:
     /// A uint of the same with as the float
     type Int: Int;
 
+    /// A int of the same with as the float
+    type SignedInt: Int;
+
     const ZERO: Self;
     const ONE: Self;
 
@@ -59,6 +62,9 @@ pub trait Float:
     /// Returns `self` transmuted to `Self::Int`
     fn repr(self) -> Self::Int;
 
+    /// Returns `self` transmuted to `Self::SignedInt`
+    fn signed_repr(self) -> Self::SignedInt;
+
     #[cfg(test)]
     /// Checks if two floats have the same bit representation. *Except* for NaNs! NaN can be
     /// represented in multiple different ways. This method returns `true` if two NaNs are
@@ -78,9 +84,10 @@ pub trait Float:
 // FIXME: Some of this can be removed if RFC Issue #1424 is resolved
 //        https://github.com/rust-lang/rfcs/issues/1424
 macro_rules! float_impl {
-    ($ty:ident, $ity:ident, $bits:expr, $significand_bits:expr) => {
+    ($ty:ident, $ity:ident, $sity:ident, $bits:expr, $significand_bits:expr) => {
         impl Float for $ty {
             type Int = $ity;
+            type SignedInt = $sity;
             const ZERO: Self = 0.0;
             const ONE: Self = 1.0;
 
@@ -95,6 +102,9 @@ macro_rules! float_impl {
             fn repr(self) -> Self::Int {
                 unsafe { mem::transmute(self) }
             }
+            fn signed_repr(self) -> Self::SignedInt {
+                unsafe { mem::transmute(self) }
+            }
             #[cfg(test)]
             fn eq_repr(self, rhs: Self) -> bool {
                 if self.is_nan() && rhs.is_nan() {
@@ -120,5 +130,5 @@ macro_rules! float_impl {
     }
 }
 
-float_impl!(f32, u32, 32, 23);
-float_impl!(f64, u64, 64, 52);
+float_impl!(f32, u32, i32, 32, 23);
+float_impl!(f64, u64, i64, 64, 52);

+ 7 - 0
tests/gedf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/gedf2.rs"));

+ 7 - 0
tests/gesf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/gesf2.rs"));

+ 7 - 0
tests/ledf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/ledf2.rs"));

+ 7 - 0
tests/lesf2.rs

@@ -0,0 +1,7 @@
+#![feature(compiler_builtins_lib)]
+#![feature(i128_type)]
+#![cfg_attr(all(target_arch = "arm", not(any(target_env = "gnu", target_env = "musl")),
+                 target_os = "linux", test),
+           no_std)]
+
+include!(concat!(env!("OUT_DIR"), "/lesf2.rs"));