diff --git a/library/compiler-builtins/src/float/mod.rs b/library/compiler-builtins/src/float/mod.rs index 5a0d37a7deb0..69e4dc635171 100644 --- a/library/compiler-builtins/src/float/mod.rs +++ b/library/compiler-builtins/src/float/mod.rs @@ -15,6 +15,7 @@ pub mod sub; #[doc(hidden)] pub trait Float: Copy + + core::fmt::Debug + PartialEq + PartialOrd + ops::AddAssign diff --git a/library/compiler-builtins/src/float/pow.rs b/library/compiler-builtins/src/float/pow.rs index 7d7f75972c71..5ab5e4201c29 100644 --- a/library/compiler-builtins/src/float/pow.rs +++ b/library/compiler-builtins/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(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; } + pow >>= 1; + if pow == 0 { + break; + } + a *= a; + } - if recip { - Self::ONE / r - } else { - r - } + if recip { + F::ONE / mul + } else { + mul } } -impl Pow for f32 {} -impl Pow for f64 {} - 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) } } diff --git a/library/compiler-builtins/testcrate/tests/misc.rs b/library/compiler-builtins/testcrate/tests/misc.rs index ec3e9d96d45a..82a1ea27b47e 100644 --- a/library/compiler-builtins/testcrate/tests/misc.rs +++ b/library/compiler-builtins/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); -}