Refactor float test macros to have a fallback
Change float test macros to fall back to testing against `rustc_apfloat` when system implementations are not available, rather than just skipping tests. This allows for easier debugging where operations may not be supported.
This commit is contained in:
parent
23b91f2b58
commit
1be6626307
5 changed files with 127 additions and 36 deletions
|
|
@ -263,3 +263,55 @@ pub fn fuzz_float_2<F: Float, E: Fn(F, F)>(n: u32, f: E) {
|
|||
f(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform an operation using builtin types if available, falling back to apfloat if not.
|
||||
#[macro_export]
|
||||
macro_rules! apfloat_fallback {
|
||||
(
|
||||
$float_ty:ty,
|
||||
// Type name in `rustc_apfloat::ieee`. Not a full path, it automatically gets the prefix.
|
||||
$apfloat_ty:ident,
|
||||
// Cfg expression for when builtin system operations should be used
|
||||
$sys_available:meta,
|
||||
// The expression to run. This expression may use `FloatTy` for its signature.
|
||||
// Optionally, the final conversion back to a float can be suppressed using
|
||||
// `=> no_convert` (for e.g. operations that return a bool).
|
||||
$op:expr $(=> $convert:ident)?,
|
||||
// Arguments that get passed to `$op` after converting to a float
|
||||
$($arg:expr),+
|
||||
$(,)?
|
||||
) => {{
|
||||
#[cfg($sys_available)]
|
||||
let ret = {
|
||||
type FloatTy = $float_ty;
|
||||
$op( $($arg),+ )
|
||||
};
|
||||
|
||||
#[cfg(not($sys_available))]
|
||||
let ret = {
|
||||
use rustc_apfloat::Float;
|
||||
type FloatTy = rustc_apfloat::ieee::$apfloat_ty;
|
||||
|
||||
let op_res = $op( $(FloatTy::from_bits($arg.to_bits().into())),+ );
|
||||
|
||||
apfloat_fallback!(@convert $float_ty, op_res $(,$convert)?)
|
||||
};
|
||||
|
||||
ret
|
||||
}};
|
||||
|
||||
// Operations that do not need converting back to a float
|
||||
(@convert $float_ty:ty, $val:expr, no_convert) => {
|
||||
$val
|
||||
};
|
||||
|
||||
// Some apfloat operations return a `StatusAnd` that we need to extract the value from. This
|
||||
// is the default.
|
||||
(@convert $float_ty:ty, $val:expr) => {{
|
||||
// ignore the status, just get the value
|
||||
let unwrapped = $val.value;
|
||||
|
||||
<$float_ty>::from_bits(FloatTy::to_bits(unwrapped).try_into().unwrap())
|
||||
}};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(unused_macros)]
|
||||
|
||||
use core::ops::{Add, Sub};
|
||||
use testcrate::*;
|
||||
|
||||
macro_rules! sum {
|
||||
|
|
@ -71,28 +72,28 @@ fn addsub() {
|
|||
}
|
||||
|
||||
macro_rules! float_sum {
|
||||
($($f:ty, $fn_add:ident, $fn_sub:ident);*;) => {
|
||||
($($f:ty, $fn_add:ident, $fn_sub:ident, $apfloat_ty:ident, $sys_available:meta);*;) => {
|
||||
$(
|
||||
fuzz_float_2(N, |x: $f, y: $f| {
|
||||
let add0 = x + y;
|
||||
let sub0 = x - y;
|
||||
let add0 = apfloat_fallback!($f, $apfloat_ty, $sys_available, Add::add, x, y);
|
||||
let sub0 = apfloat_fallback!($f, $apfloat_ty, $sys_available, Sub::sub, x, y);
|
||||
let add1: $f = $fn_add(x, y);
|
||||
let sub1: $f = $fn_sub(x, y);
|
||||
if !Float::eq_repr(add0, add1) {
|
||||
panic!(
|
||||
"{}({}, {}): std: {}, builtins: {}",
|
||||
"{}({:?}, {:?}): std: {:?}, builtins: {:?}",
|
||||
stringify!($fn_add), x, y, add0, add1
|
||||
);
|
||||
}
|
||||
if !Float::eq_repr(sub0, sub1) {
|
||||
panic!(
|
||||
"{}({}, {}): std: {}, builtins: {}",
|
||||
"{}({:?}, {:?}): std: {:?}, builtins: {:?}",
|
||||
stringify!($fn_sub), x, y, sub0, sub1
|
||||
);
|
||||
}
|
||||
});
|
||||
)*
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "x86", not(target_feature = "sse"))))]
|
||||
|
|
@ -105,8 +106,8 @@ fn float_addsub() {
|
|||
};
|
||||
|
||||
float_sum!(
|
||||
f32, __addsf3, __subsf3;
|
||||
f64, __adddf3, __subdf3;
|
||||
f32, __addsf3, __subsf3, Single, all();
|
||||
f64, __adddf3, __subdf3, Double, all();
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +121,7 @@ fn float_addsub_arm() {
|
|||
};
|
||||
|
||||
float_sum!(
|
||||
f32, __addsf3vfp, __subsf3vfp;
|
||||
f64, __adddf3vfp, __subdf3vfp;
|
||||
f32, __addsf3vfp, __subsf3vfp, Single, all();
|
||||
f64, __adddf3vfp, __subdf3vfp, Double, all();
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,48 @@
|
|||
#![allow(unused_macros)]
|
||||
#![allow(unreachable_code)]
|
||||
|
||||
#[cfg(not(target_arch = "powerpc64"))]
|
||||
use testcrate::*;
|
||||
|
||||
macro_rules! cmp {
|
||||
($x:ident, $y:ident, $($unordered_val:expr, $fn:ident);*;) => {
|
||||
(
|
||||
$f:ty, $x:ident, $y:ident, $apfloat_ty:ident, $sys_available:meta,
|
||||
$($unordered_val:expr, $fn:ident);*;
|
||||
) => {
|
||||
$(
|
||||
let cmp0 = if $x.is_nan() || $y.is_nan() {
|
||||
let cmp0 = if apfloat_fallback!(
|
||||
$f, $apfloat_ty, $sys_available,
|
||||
|x: FloatTy| x.is_nan() => no_convert,
|
||||
$x
|
||||
) || apfloat_fallback!(
|
||||
$f, $apfloat_ty, $sys_available,
|
||||
|y: FloatTy| y.is_nan() => no_convert,
|
||||
$y
|
||||
)
|
||||
{
|
||||
$unordered_val
|
||||
} else if $x < $y {
|
||||
} else if apfloat_fallback!(
|
||||
$f, $apfloat_ty, $sys_available,
|
||||
|x, y| x < y => no_convert,
|
||||
$x, $y
|
||||
) {
|
||||
-1
|
||||
} else if $x == $y {
|
||||
} else if apfloat_fallback!(
|
||||
$f, $apfloat_ty, $sys_available,
|
||||
|x, y| x == y => no_convert,
|
||||
$x, $y
|
||||
) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let cmp1 = $fn($x, $y);
|
||||
if cmp0 != cmp1 {
|
||||
panic!("{}({}, {}): std: {}, builtins: {}", stringify!($fn_builtins), $x, $y, cmp0, cmp1);
|
||||
panic!(
|
||||
"{}({:?}, {:?}): std: {:?}, builtins: {:?}",
|
||||
stringify!($fn), $x, $y, cmp0, cmp1
|
||||
);
|
||||
}
|
||||
)*
|
||||
};
|
||||
|
|
@ -34,7 +59,7 @@ fn float_comparisons() {
|
|||
|
||||
fuzz_float_2(N, |x: f32, y: f32| {
|
||||
assert_eq!(__unordsf2(x, y) != 0, x.is_nan() || y.is_nan());
|
||||
cmp!(x, y,
|
||||
cmp!(f32, x, y, Single, all(),
|
||||
1, __ltsf2;
|
||||
1, __lesf2;
|
||||
1, __eqsf2;
|
||||
|
|
@ -45,7 +70,7 @@ fn float_comparisons() {
|
|||
});
|
||||
fuzz_float_2(N, |x: f64, y: f64| {
|
||||
assert_eq!(__unorddf2(x, y) != 0, x.is_nan() || y.is_nan());
|
||||
cmp!(x, y,
|
||||
cmp!(f64, x, y, Double, all(),
|
||||
1, __ltdf2;
|
||||
1, __ledf2;
|
||||
1, __eqdf2;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use compiler_builtins::int::sdiv::{__divmoddi4, __divmodsi4, __divmodti4};
|
||||
use compiler_builtins::int::udiv::{__udivmoddi4, __udivmodsi4, __udivmodti4, u128_divide_sparc};
|
||||
|
||||
use testcrate::*;
|
||||
|
||||
// Division algorithms have by far the nastiest and largest number of edge cases, and experience shows
|
||||
|
|
@ -104,16 +105,20 @@ fn divide_sparc() {
|
|||
}
|
||||
|
||||
macro_rules! float {
|
||||
($($i:ty, $fn:ident);*;) => {
|
||||
($($f:ty, $fn:ident, $apfloat_ty:ident, $sys_available:meta);*;) => {
|
||||
$(
|
||||
fuzz_float_2(N, |x: $i, y: $i| {
|
||||
let quo0 = x / y;
|
||||
let quo1: $i = $fn(x, y);
|
||||
fuzz_float_2(N, |x: $f, y: $f| {
|
||||
let quo0: $f = apfloat_fallback!($f, $apfloat_ty, $sys_available, Div::div, x, y);
|
||||
let quo1: $f = $fn(x, y);
|
||||
#[cfg(not(target_arch = "arm"))]
|
||||
if !Float::eq_repr(quo0, quo1) {
|
||||
panic!(
|
||||
"{}({}, {}): std: {}, builtins: {}",
|
||||
stringify!($fn), x, y, quo0, quo1
|
||||
"{}({:?}, {:?}): std: {:?}, builtins: {:?}",
|
||||
stringify!($fn),
|
||||
x,
|
||||
y,
|
||||
quo0,
|
||||
quo1
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -122,8 +127,12 @@ macro_rules! float {
|
|||
if !(Float::is_subnormal(quo0) || Float::is_subnormal(quo1)) {
|
||||
if !Float::eq_repr(quo0, quo1) {
|
||||
panic!(
|
||||
"{}({}, {}): std: {}, builtins: {}",
|
||||
stringify!($fn), x, y, quo0, quo1
|
||||
"{}({:?}, {:?}): std: {:?}, builtins: {:?}",
|
||||
stringify!($fn),
|
||||
x,
|
||||
y,
|
||||
quo0,
|
||||
quo1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -139,10 +148,11 @@ fn float_div() {
|
|||
div::{__divdf3, __divsf3},
|
||||
Float,
|
||||
};
|
||||
use core::ops::Div;
|
||||
|
||||
float!(
|
||||
f32, __divsf3;
|
||||
f64, __divdf3;
|
||||
f32, __divsf3, Single, all();
|
||||
f64, __divdf3, Double, all();
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -153,9 +163,10 @@ fn float_div_arm() {
|
|||
div::{__divdf3vfp, __divsf3vfp},
|
||||
Float,
|
||||
};
|
||||
use core::ops::Div;
|
||||
|
||||
float!(
|
||||
f32, __divsf3vfp;
|
||||
f64, __divdf3vfp;
|
||||
f32, __divsf3vfp, Single, all();
|
||||
f64, __divdf3vfp, Double, all();
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,16 +82,16 @@ fn overflowing_mul() {
|
|||
}
|
||||
|
||||
macro_rules! float_mul {
|
||||
($($f:ty, $fn:ident);*;) => {
|
||||
($($f:ty, $fn:ident, $apfloat_ty:ident, $sys_available:meta);*;) => {
|
||||
$(
|
||||
fuzz_float_2(N, |x: $f, y: $f| {
|
||||
let mul0 = x * y;
|
||||
let mul0 = apfloat_fallback!($f, $apfloat_ty, $sys_available, Mul::mul, x, y);
|
||||
let mul1: $f = $fn(x, y);
|
||||
// multiplication of subnormals is not currently handled
|
||||
if !(Float::is_subnormal(mul0) || Float::is_subnormal(mul1)) {
|
||||
if !Float::eq_repr(mul0, mul1) {
|
||||
panic!(
|
||||
"{}({}, {}): std: {}, builtins: {}",
|
||||
"{}({:?}, {:?}): std: {:?}, builtins: {:?}",
|
||||
stringify!($fn), x, y, mul0, mul1
|
||||
);
|
||||
}
|
||||
|
|
@ -108,10 +108,11 @@ fn float_mul() {
|
|||
mul::{__muldf3, __mulsf3},
|
||||
Float,
|
||||
};
|
||||
use core::ops::Mul;
|
||||
|
||||
float_mul!(
|
||||
f32, __mulsf3;
|
||||
f64, __muldf3;
|
||||
f32, __mulsf3, Single, all();
|
||||
f64, __muldf3, Double, all();
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -122,9 +123,10 @@ fn float_mul_arm() {
|
|||
mul::{__muldf3vfp, __mulsf3vfp},
|
||||
Float,
|
||||
};
|
||||
use core::ops::Mul;
|
||||
|
||||
float_mul!(
|
||||
f32, __mulsf3vfp;
|
||||
f64, __muldf3vfp;
|
||||
f32, __mulsf3vfp, Single, all();
|
||||
f64, __muldf3vfp, Double, all();
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue