浏览代码

fix `powi`

Aaron Kutch 4 年之前
父节点
当前提交
adb7096157
共有 3 个文件被更改,包括 51 次插入44 次删除
  1. 1 0
      src/float/mod.rs
  2. 22 25
      src/float/pow.rs
  3. 28 19
      testcrate/tests/misc.rs

+ 1 - 0
src/float/mod.rs

@@ -15,6 +15,7 @@ pub mod sub;
 #[doc(hidden)]
 pub trait Float:
     Copy
+    + core::fmt::Debug
     + PartialEq
     + PartialOrd
     + ops::AddAssign

+ 22 - 25
src/float/pow.rs

@@ -1,39 +1,36 @@
 use float::Float;
+use int::Int;
 
-trait Pow: Float {
-    /// Returns `a` raised to the power `b`
-    fn pow(self, mut b: i32) -> Self {
-        let mut a = self;
-        let recip = b < 0;
-        let mut r = Self::ONE;
-        loop {
-            if (b & 1) != 0 {
-                r *= a;
-            }
-            b = ((b as u32) >> 1) as i32;
-            if b == 0 {
-                break;
-            }
-            a *= a;
+/// Returns `a` raised to the power `b`
+fn pow<F: Float>(a: F, b: i32) -> F {
+    let mut a = a;
+    let recip = b < 0;
+    let mut pow = i32::abs_diff(b, 0);
+    let mut mul = F::ONE;
+    loop {
+        if (pow & 1) != 0 {
+            mul *= a;
         }
-
-        if recip {
-            Self::ONE / r
-        } else {
-            r
+        pow >>= 1;
+        if pow == 0 {
+            break;
         }
+        a *= a;
     }
-}
 
-impl Pow for f32 {}
-impl Pow for f64 {}
+    if recip {
+        F::ONE / mul
+    } else {
+        mul
+    }
+}
 
 intrinsics! {
     pub extern "C" fn __powisf2(a: f32, b: i32) -> f32 {
-        a.pow(b)
+        pow(a, b)
     }
 
     pub extern "C" fn __powidf2(a: f64, b: i32) -> f64 {
-        a.pow(b)
+        pow(a, b)
     }
 }

+ 28 - 19
testcrate/tests/misc.rs

@@ -126,22 +126,39 @@ fn float_extend_arm() {
     extend!(f32, f64, __extendsfdf2vfp);
 }
 
-// This doesn't quite work because of issues related to
+// This is approximate because of issues related to
 // https://github.com/rust-lang/rust/issues/73920.
-// TODO how do we resolve this?
-/*
+// TODO how do we resolve this indeterminacy?
 macro_rules! pow {
-    ($($f:ty, $fn:ident);*;) => {
+    ($($f:ty, $tolerance:expr, $fn:ident);*;) => {
         $(
             fuzz_float_2(N, |x: $f, y: $f| {
-                if !(Float::is_subnormal(x) || Float::is_subnormal(y) || x < 0. || y < 0.) {
-                    let n = y as i32;
+                if !(Float::is_subnormal(x) || Float::is_subnormal(y) || x.is_nan()) {
+                    let n = y.to_bits() & !<$f as Float>::SIGNIFICAND_MASK;
+                    let n = (n as <$f as Float>::SignedInt) >> <$f as Float>::SIGNIFICAND_BITS;
+                    let n = n as i32;
                     let tmp0: $f = x.powi(n);
                     let tmp1: $f = $fn(x, n);
-                    if tmp0 != tmp1 {
+                    let (a, b) = if tmp0 < tmp1 {
+                        (tmp0, tmp1)
+                    } else {
+                        (tmp1, tmp0)
+                    };
+                    let good = {
+                        if a == b {
+                            // handles infinity equality
+                            true
+                        } else if a < $tolerance {
+                            b < $tolerance
+                        } else {
+                            let quo = b / a;
+                            (quo < (1. + $tolerance)) && (quo > (1. - $tolerance))
+                        }
+                    };
+                    if !good {
                         panic!(
                             "{}({}, {}): std: {}, builtins: {}",
-                            stringify!($fn), x, y, tmp0, tmp1
+                            stringify!($fn), x, n, tmp0, tmp1
                         );
                     }
                 }
@@ -150,21 +167,13 @@ macro_rules! pow {
     };
 }
 
+#[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))]
 #[test]
 fn float_pow() {
     use compiler_builtins::float::pow::{__powidf2, __powisf2};
 
     pow!(
-        f32, __powisf2;
-        f64, __powidf2;
+        f32, 1e-4, __powisf2;
+        f64, 1e-12, __powidf2;
     );
 }
-*/
-
-// placeholder test to make sure basic functionality works
-#[test]
-fn float_pow() {
-    use compiler_builtins::float::pow::{__powidf2, __powisf2};
-    assert_eq!(__powisf2(-3.0, 3), -27.0);
-    assert_eq!(__powidf2(-3.0, 3), -27.0);
-}