mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-10 22:13:27 +00:00
Add proptest float tests
This commit is contained in:
parent
d3c58daa96
commit
d5c227998b
@ -2,4 +2,5 @@
|
||||
|
||||
members = [
|
||||
"crates/core_simd",
|
||||
"crates/test_helpers",
|
||||
]
|
||||
|
@ -14,3 +14,11 @@ version = "0.2"
|
||||
|
||||
[dev-dependencies.wasm-bindgen-test]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.proptest]
|
||||
version = "0.10"
|
||||
default-features = false
|
||||
features = ["alloc"]
|
||||
|
||||
[dev-dependencies.test_helpers]
|
||||
path = "../test_helpers"
|
||||
|
@ -141,6 +141,12 @@ macro_rules! impl_vector {
|
||||
}
|
||||
}
|
||||
|
||||
impl <const LANES: usize> From<$name<LANES>> for [$type; LANES] {
|
||||
fn from(vector: $name<LANES>) -> Self {
|
||||
vector.0
|
||||
}
|
||||
}
|
||||
|
||||
// splat
|
||||
impl<const LANES: usize> From<$type> for $name<LANES> where Self: crate::LanesAtMost64 {
|
||||
#[inline]
|
||||
|
132
crates/core_simd/tests/float.rs
Normal file
132
crates/core_simd/tests/float.rs
Normal file
@ -0,0 +1,132 @@
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
macro_rules! impl_op_test {
|
||||
{ unary, $vector:ty, $scalar:ty, $trait:ident :: $fn:ident } => {
|
||||
test_helpers::test_lanes! {
|
||||
fn $fn<const LANES: usize>() {
|
||||
test_helpers::test_unary_elementwise(
|
||||
<$vector as core::ops::$trait>::$fn,
|
||||
<$scalar as core::ops::$trait>::$fn,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
{ binary, $vector:ty, $scalar:ty, $trait:ident :: $fn:ident, $trait_assign:ident :: $fn_assign:ident } => {
|
||||
mod $fn {
|
||||
use super::*;
|
||||
|
||||
test_helpers::test_lanes! {
|
||||
fn normal<const LANES: usize>() {
|
||||
test_helpers::test_binary_elementwise(
|
||||
<$vector as core::ops::$trait>::$fn,
|
||||
<$scalar as core::ops::$trait>::$fn,
|
||||
);
|
||||
}
|
||||
|
||||
fn scalar_rhs<const LANES: usize>() {
|
||||
test_helpers::test_binary_scalar_rhs_elementwise(
|
||||
<$vector as core::ops::$trait<$scalar>>::$fn,
|
||||
<$scalar as core::ops::$trait>::$fn,
|
||||
);
|
||||
}
|
||||
|
||||
fn scalar_lhs<const LANES: usize>() {
|
||||
test_helpers::test_binary_scalar_lhs_elementwise(
|
||||
<$scalar as core::ops::$trait<$vector>>::$fn,
|
||||
<$scalar as core::ops::$trait>::$fn,
|
||||
);
|
||||
}
|
||||
|
||||
fn assign<const LANES: usize>() {
|
||||
test_helpers::test_binary_elementwise(
|
||||
|mut a, b| { <$vector as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
|
||||
|mut a, b| { <$scalar as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
|
||||
)
|
||||
}
|
||||
|
||||
fn assign_scalar_rhs<const LANES: usize>() {
|
||||
test_helpers::test_binary_scalar_rhs_elementwise(
|
||||
|mut a, b| { <$vector as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
|
||||
|mut a, b| { <$scalar as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_tests {
|
||||
{ $vector:ident, $scalar:tt, $int_scalar:tt } => {
|
||||
mod $scalar {
|
||||
type Vector<const LANES: usize> = core_simd::$vector<LANES>;
|
||||
type Scalar = $scalar;
|
||||
type IntScalar = $int_scalar;
|
||||
|
||||
impl_op_test! { unary, Vector<LANES>, Scalar, Neg::neg }
|
||||
impl_op_test! { binary, Vector<LANES>, Scalar, Add::add, AddAssign::add_assign }
|
||||
impl_op_test! { binary, Vector<LANES>, Scalar, Sub::sub, SubAssign::sub_assign }
|
||||
impl_op_test! { binary, Vector<LANES>, Scalar, Mul::mul, SubAssign::sub_assign }
|
||||
impl_op_test! { binary, Vector<LANES>, Scalar, Div::div, DivAssign::div_assign }
|
||||
impl_op_test! { binary, Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign }
|
||||
|
||||
test_helpers::test_lanes! {
|
||||
fn abs<const LANES: usize>() {
|
||||
test_helpers::test_unary_elementwise(
|
||||
Vector::<LANES>::abs,
|
||||
Scalar::abs,
|
||||
)
|
||||
}
|
||||
|
||||
fn ceil<const LANES: usize>() {
|
||||
test_helpers::test_unary_elementwise(
|
||||
Vector::<LANES>::ceil,
|
||||
Scalar::ceil,
|
||||
)
|
||||
}
|
||||
|
||||
fn floor<const LANES: usize>() {
|
||||
test_helpers::test_unary_elementwise(
|
||||
Vector::<LANES>::floor,
|
||||
Scalar::floor,
|
||||
)
|
||||
}
|
||||
|
||||
fn round_from_int<const LANES: usize>() {
|
||||
test_helpers::test_unary_elementwise(
|
||||
Vector::<LANES>::round_from_int,
|
||||
|x| x as Scalar,
|
||||
)
|
||||
}
|
||||
|
||||
fn to_int_unchecked<const LANES: usize>() {
|
||||
// The maximum integer that can be represented by the equivalently sized float has
|
||||
// all of the mantissa digits set to 1, pushed up to the MSB.
|
||||
const ALL_MANTISSA_BITS: IntScalar = ((1 << <Scalar>::MANTISSA_DIGITS) - 1);
|
||||
const MAX_REPRESENTABLE_VALUE: Scalar =
|
||||
(ALL_MANTISSA_BITS << (core::mem::size_of::<Scalar>() * 8 - <Scalar>::MANTISSA_DIGITS as usize - 1)) as Scalar;
|
||||
|
||||
let mut runner = proptest::test_runner::TestRunner::default();
|
||||
runner.run(
|
||||
&test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE),
|
||||
|x| {
|
||||
let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() };
|
||||
let result_2 = {
|
||||
let mut result = [0; LANES];
|
||||
for (i, o) in x.iter().zip(result.iter_mut()) {
|
||||
*o = unsafe { i.to_int_unchecked() };
|
||||
}
|
||||
result
|
||||
};
|
||||
test_helpers::prop_assert_biteq!(result_1, result_2);
|
||||
Ok(())
|
||||
},
|
||||
).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_tests! { SimdF32, f32, i32 }
|
||||
impl_tests! { SimdF64, f64, i64 }
|
@ -1,6 +0,0 @@
|
||||
use super::helpers;
|
||||
|
||||
float_tests! { f32x2, f32, i32x2, i32 }
|
||||
float_tests! { f32x4, f32, i32x4, i32 }
|
||||
float_tests! { f32x8, f32, i32x8, i32 }
|
||||
float_tests! { f32x16, f32, i32x16, i32 }
|
@ -1,5 +0,0 @@
|
||||
use super::helpers;
|
||||
|
||||
float_tests! { f64x2, f64, i64x2, i64 }
|
||||
float_tests! { f64x4, f64, i64x4, i64 }
|
||||
float_tests! { f64x8, f64, i64x8, i64 }
|
@ -1,418 +0,0 @@
|
||||
macro_rules! float_tests {
|
||||
{ $vector:ident, $scalar:ident, $int_vector:ident, $int_scalar:ident } => {
|
||||
#[cfg(test)]
|
||||
mod $vector {
|
||||
use super::*;
|
||||
use helpers::lanewise::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
// TODO impl this as an associated fn on vectors
|
||||
fn from_slice(slice: &[$scalar]) -> core_simd::$vector {
|
||||
let mut value = core_simd::$vector::default();
|
||||
let value_slice: &mut [_] = value.as_mut();
|
||||
value_slice.copy_from_slice(&slice[0..value_slice.len()]);
|
||||
value
|
||||
}
|
||||
|
||||
fn slice_chunks(slice: &[$scalar]) -> impl Iterator<Item = core_simd::$vector> + '_ {
|
||||
let lanes = core::mem::size_of::<core_simd::$vector>() / core::mem::size_of::<$scalar>();
|
||||
slice.chunks_exact(lanes).map(from_slice)
|
||||
}
|
||||
|
||||
fn from_slice_int(slice: &[$int_scalar]) -> core_simd::$int_vector {
|
||||
let mut value = core_simd::$int_vector::default();
|
||||
let value_slice: &mut [_] = value.as_mut();
|
||||
value_slice.copy_from_slice(&slice[0..value_slice.len()]);
|
||||
value
|
||||
}
|
||||
|
||||
fn slice_chunks_int(slice: &[$int_scalar]) -> impl Iterator<Item = core_simd::$int_vector> + '_ {
|
||||
let lanes = core::mem::size_of::<core_simd::$int_vector>() / core::mem::size_of::<$int_scalar>();
|
||||
slice.chunks_exact(lanes).map(from_slice_int)
|
||||
}
|
||||
|
||||
const A: [$scalar; 16] = [0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15.];
|
||||
const B: [$scalar; 16] = [16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31.];
|
||||
const C: [$scalar; 16] = [
|
||||
-0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
1.0,
|
||||
<$scalar>::MIN,
|
||||
<$scalar>::MAX,
|
||||
<$scalar>::INFINITY,
|
||||
<$scalar>::NEG_INFINITY,
|
||||
<$scalar>::MIN_POSITIVE,
|
||||
-<$scalar>::MIN_POSITIVE,
|
||||
<$scalar>::EPSILON,
|
||||
-<$scalar>::EPSILON,
|
||||
<$scalar>::NAN,
|
||||
-<$scalar>::NAN,
|
||||
// TODO: Would be nice to check sNaN...
|
||||
100.0 / 3.0,
|
||||
-100.0 / 3.0,
|
||||
];
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn add() {
|
||||
let a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Add::add);
|
||||
assert_biteq!(a + b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn add_assign() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Add::add);
|
||||
a += b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn add_scalar_rhs() {
|
||||
let a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Add::add);
|
||||
assert_biteq!(a + b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn add_scalar_lhs() {
|
||||
let a = 5.;
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Add::add);
|
||||
assert_biteq!(a + b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn add_assign_scalar() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Add::add);
|
||||
a += b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn sub() {
|
||||
let a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Sub::sub);
|
||||
assert_biteq!(a - b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn sub_assign() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Sub::sub);
|
||||
a -= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn sub_scalar_rhs() {
|
||||
let a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Sub::sub);
|
||||
assert_biteq!(a - b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn sub_scalar_lhs() {
|
||||
let a = 5.;
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Sub::sub);
|
||||
assert_biteq!(a - b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn sub_assign_scalar() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Sub::sub);
|
||||
a -= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn mul() {
|
||||
let a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Mul::mul);
|
||||
assert_biteq!(a * b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn mul_assign() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Mul::mul);
|
||||
a *= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn mul_scalar_rhs() {
|
||||
let a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Mul::mul);
|
||||
assert_biteq!(a * b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn mul_scalar_lhs() {
|
||||
let a = 5.;
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Mul::mul);
|
||||
assert_biteq!(a * b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn mul_assign_scalar() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Mul::mul);
|
||||
a *= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn div() {
|
||||
let a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Div::div);
|
||||
assert_biteq!(a / b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn div_assign() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Div::div);
|
||||
a /= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn div_scalar_rhs() {
|
||||
let a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Div::div);
|
||||
assert_biteq!(a / b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn div_scalar_lhs() {
|
||||
let a = 5.;
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Div::div);
|
||||
assert_biteq!(a / b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn div_assign_scalar() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Div::div);
|
||||
a /= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn rem() {
|
||||
let a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Rem::rem);
|
||||
assert_biteq!(a % b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn rem_assign() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_lanewise(a, b, core::ops::Rem::rem);
|
||||
a %= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn rem_scalar_rhs() {
|
||||
let a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Rem::rem);
|
||||
assert_biteq!(a % b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn rem_scalar_lhs() {
|
||||
let a = 5.;
|
||||
let b = from_slice(&B);
|
||||
let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Rem::rem);
|
||||
assert_biteq!(a % b, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn rem_assign_scalar() {
|
||||
let mut a = from_slice(&A);
|
||||
let b = 5.;
|
||||
let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Rem::rem);
|
||||
a %= b;
|
||||
assert_biteq!(a, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn neg() {
|
||||
let v = from_slice(&A);
|
||||
let expected = apply_unary_lanewise(v, core::ops::Neg::neg);
|
||||
assert_biteq!(-v, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn neg_odd_floats() {
|
||||
for v in slice_chunks(&C) {
|
||||
let expected = apply_unary_lanewise(v, core::ops::Neg::neg);
|
||||
assert_biteq!(-v, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn abs_negative() {
|
||||
let v = -from_slice(&A);
|
||||
let expected = apply_unary_lanewise(v, <$scalar>::abs);
|
||||
assert_biteq!(v.abs(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn abs_positive() {
|
||||
let v = from_slice(&B);
|
||||
let expected = apply_unary_lanewise(v, <$scalar>::abs);
|
||||
assert_biteq!(v.abs(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn abs_odd_floats() {
|
||||
for v in slice_chunks(&C) {
|
||||
let expected = apply_unary_lanewise(v, <$scalar>::abs);
|
||||
assert_biteq!(v.abs(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn ceil_odd_floats() {
|
||||
for v in slice_chunks(&C) {
|
||||
let expected = apply_unary_lanewise(v, <$scalar>::ceil);
|
||||
assert_biteq!(v.ceil(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn floor_odd_floats() {
|
||||
for v in slice_chunks(&C) {
|
||||
let expected = apply_unary_lanewise(v, <$scalar>::floor);
|
||||
assert_biteq!(v.floor(), expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn to_int_unchecked() {
|
||||
// The maximum integer that can be represented by the equivalently sized float has
|
||||
// all of the mantissa digits set to 1, pushed up to the MSB.
|
||||
const ALL_MANTISSA_BITS: $int_scalar = ((1 << <$scalar>::MANTISSA_DIGITS) - 1);
|
||||
const MAX_REPRESENTABLE_VALUE: $int_scalar =
|
||||
ALL_MANTISSA_BITS << (core::mem::size_of::<$scalar>() * 8 - <$scalar>::MANTISSA_DIGITS as usize - 1);
|
||||
const VALUES: [$scalar; 16] = [
|
||||
-0.0,
|
||||
0.0,
|
||||
-1.0,
|
||||
1.0,
|
||||
ALL_MANTISSA_BITS as $scalar,
|
||||
-ALL_MANTISSA_BITS as $scalar,
|
||||
MAX_REPRESENTABLE_VALUE as $scalar,
|
||||
-MAX_REPRESENTABLE_VALUE as $scalar,
|
||||
(MAX_REPRESENTABLE_VALUE / 2) as $scalar,
|
||||
(-MAX_REPRESENTABLE_VALUE / 2) as $scalar,
|
||||
<$scalar>::MIN_POSITIVE,
|
||||
-<$scalar>::MIN_POSITIVE,
|
||||
<$scalar>::EPSILON,
|
||||
-<$scalar>::EPSILON,
|
||||
100.0 / 3.0,
|
||||
-100.0 / 3.0,
|
||||
];
|
||||
|
||||
for v in slice_chunks(&VALUES) {
|
||||
let expected = apply_unary_lanewise(v, |x| unsafe { x.to_int_unchecked() });
|
||||
assert_biteq!(unsafe { v.to_int_unchecked() }, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn round_from_int() {
|
||||
const VALUES: [$int_scalar; 16] = [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
-1,
|
||||
100,
|
||||
-100,
|
||||
200,
|
||||
-200,
|
||||
413,
|
||||
-413,
|
||||
1017,
|
||||
-1017,
|
||||
1234567,
|
||||
-1234567,
|
||||
<$int_scalar>::MAX,
|
||||
<$int_scalar>::MIN,
|
||||
];
|
||||
|
||||
for v in slice_chunks_int(&VALUES) {
|
||||
let expected = apply_unary_lanewise(v, |x| x as $scalar);
|
||||
assert_biteq!(core_simd::$vector::round_from_int(v), expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,12 +2,6 @@
|
||||
#[path = "../helpers/mod.rs"]
|
||||
mod helpers;
|
||||
|
||||
#[macro_use]
|
||||
mod float_macros;
|
||||
|
||||
mod r#f32;
|
||||
mod r#f64;
|
||||
|
||||
#[macro_use]
|
||||
mod int_macros;
|
||||
|
||||
|
9
crates/test_helpers/Cargo.toml
Normal file
9
crates/test_helpers/Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "test_helpers"
|
||||
version = "0.1.0"
|
||||
authors = ["Caleb Zulawski <caleb.zulawski@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
proptest = "0.10"
|
98
crates/test_helpers/src/array.rs
Normal file
98
crates/test_helpers/src/array.rs
Normal file
@ -0,0 +1,98 @@
|
||||
// Adapted from proptest's array code
|
||||
// Copyright 2017 Jason Lingle
|
||||
|
||||
use proptest::{
|
||||
strategy::{NewTree, Strategy, ValueTree},
|
||||
test_runner::TestRunner,
|
||||
};
|
||||
use core::{
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
};
|
||||
|
||||
#[must_use = "strategies do nothing unless used"]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UniformArrayStrategy<S, T> {
|
||||
strategy: S,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<S, T> UniformArrayStrategy<S, T> {
|
||||
pub fn new(strategy: S) -> Self {
|
||||
Self {
|
||||
strategy,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrayValueTree<T> {
|
||||
tree: T,
|
||||
shrinker: usize,
|
||||
last_shrinker: Option<usize>,
|
||||
}
|
||||
|
||||
impl<T, S, const LANES: usize> Strategy for UniformArrayStrategy<S, [T; LANES]>
|
||||
where
|
||||
T: core::fmt::Debug,
|
||||
S: Strategy<Value = T>,
|
||||
{
|
||||
type Tree = ArrayValueTree<[S::Tree; LANES]>;
|
||||
type Value = [T; LANES];
|
||||
|
||||
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
|
||||
let tree: [S::Tree; LANES] = unsafe {
|
||||
let mut tree: [MaybeUninit<S::Tree>; LANES] = MaybeUninit::uninit().assume_init();
|
||||
for t in tree.iter_mut() {
|
||||
*t = MaybeUninit::new(self.strategy.new_tree(runner)?)
|
||||
}
|
||||
core::mem::transmute_copy(&tree)
|
||||
};
|
||||
Ok(ArrayValueTree {
|
||||
tree,
|
||||
shrinker: 0,
|
||||
last_shrinker: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ValueTree, const LANES: usize> ValueTree for ArrayValueTree<[T; LANES]> {
|
||||
type Value = [T::Value; LANES];
|
||||
|
||||
fn current(&self) -> Self::Value {
|
||||
unsafe {
|
||||
let mut value: [MaybeUninit<T::Value>; LANES] = MaybeUninit::uninit().assume_init();
|
||||
for (tree_elem, value_elem) in self.tree.iter().zip(value.iter_mut()) {
|
||||
*value_elem = MaybeUninit::new(tree_elem.current());
|
||||
}
|
||||
core::mem::transmute_copy(&value)
|
||||
}
|
||||
}
|
||||
|
||||
fn simplify(&mut self) -> bool {
|
||||
while self.shrinker < LANES {
|
||||
if self.tree[self.shrinker].simplify() {
|
||||
self.last_shrinker = Some(self.shrinker);
|
||||
return true;
|
||||
} else {
|
||||
self.shrinker += 1;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn complicate(&mut self) -> bool {
|
||||
if let Some(shrinker) = self.last_shrinker {
|
||||
self.shrinker = shrinker;
|
||||
if self.tree[shrinker].complicate() {
|
||||
true
|
||||
} else {
|
||||
self.last_shrinker = None;
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
94
crates/test_helpers/src/biteq.rs
Normal file
94
crates/test_helpers/src/biteq.rs
Normal file
@ -0,0 +1,94 @@
|
||||
pub trait BitEq {
|
||||
fn biteq(&self, other: &Self) -> bool;
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result;
|
||||
}
|
||||
|
||||
macro_rules! impl_integer_biteq {
|
||||
{ $($type:ty),* } => {
|
||||
$(
|
||||
impl BitEq for $type {
|
||||
fn biteq(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{:?} ({:x})", self, self)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_integer_biteq! { u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize }
|
||||
|
||||
macro_rules! impl_float_biteq {
|
||||
{ $($type:ty),* } => {
|
||||
$(
|
||||
impl BitEq for $type {
|
||||
fn biteq(&self, other: &Self) -> bool {
|
||||
if self.is_nan() && other.is_nan() {
|
||||
true // exact nan bits don't matter
|
||||
} else {
|
||||
self.to_bits() == other.to_bits()
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
write!(f, "{:?} ({:x})", self, self.to_bits())
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_float_biteq! { f32, f64 }
|
||||
|
||||
impl<T: BitEq, const N: usize> BitEq for [T; N] {
|
||||
fn biteq(&self, other: &Self) -> bool {
|
||||
self.iter()
|
||||
.zip(other.iter())
|
||||
.fold(true, |value, (left, right)| value && left.biteq(right))
|
||||
}
|
||||
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
#[repr(transparent)]
|
||||
struct Wrapper<'a, T: BitEq>(&'a T);
|
||||
|
||||
impl<T: BitEq> core::fmt::Debug for Wrapper<'_, T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_list()
|
||||
.entries(self.iter().map(|x| Wrapper(x)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct BitEqWrapper<'a, T>(pub &'a T);
|
||||
|
||||
impl<T: BitEq> PartialEq for BitEqWrapper<'_, T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0.biteq(other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BitEq> core::fmt::Debug for BitEqWrapper<'_, T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! prop_assert_biteq {
|
||||
{ $a:expr, $b:expr } => {
|
||||
{
|
||||
use $crate::biteq::BitEqWrapper;
|
||||
let a = $a;
|
||||
let b = $b;
|
||||
proptest::prop_assert_eq!(BitEqWrapper(&a), BitEqWrapper(&b));
|
||||
}
|
||||
}
|
||||
}
|
224
crates/test_helpers/src/lib.rs
Normal file
224
crates/test_helpers/src/lib.rs
Normal file
@ -0,0 +1,224 @@
|
||||
pub mod array;
|
||||
|
||||
#[macro_use]
|
||||
pub mod biteq;
|
||||
|
||||
pub trait DefaultStrategy {
|
||||
type Strategy: proptest::strategy::Strategy<Value = Self>;
|
||||
fn default_strategy() -> Self::Strategy;
|
||||
}
|
||||
|
||||
macro_rules! impl_num {
|
||||
{ $type:tt } => {
|
||||
impl DefaultStrategy for $type {
|
||||
type Strategy = proptest::num::$type::Any;
|
||||
fn default_strategy() -> Self::Strategy {
|
||||
proptest::num::$type::ANY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_num! { i8 }
|
||||
impl_num! { i16 }
|
||||
impl_num! { i32 }
|
||||
impl_num! { i64 }
|
||||
impl_num! { i128 }
|
||||
impl_num! { isize }
|
||||
impl_num! { u8 }
|
||||
impl_num! { u16 }
|
||||
impl_num! { u32 }
|
||||
impl_num! { u64 }
|
||||
impl_num! { u128 }
|
||||
impl_num! { usize }
|
||||
impl_num! { f32 }
|
||||
impl_num! { f64 }
|
||||
|
||||
impl<T: core::fmt::Debug + DefaultStrategy, const LANES: usize> DefaultStrategy for [T; LANES] {
|
||||
type Strategy = crate::array::UniformArrayStrategy<T::Strategy, Self>;
|
||||
fn default_strategy() -> Self::Strategy {
|
||||
Self::Strategy::new(T::default_strategy())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn test_1<A: core::fmt::Debug + DefaultStrategy>(
|
||||
f: impl Fn(A) -> proptest::test_runner::TestCaseResult,
|
||||
) {
|
||||
let mut runner = proptest::test_runner::TestRunner::default();
|
||||
runner.run(&A::default_strategy(), f).unwrap();
|
||||
}
|
||||
|
||||
pub fn test_2<A: core::fmt::Debug + DefaultStrategy, B: core::fmt::Debug + DefaultStrategy>(
|
||||
f: impl Fn(A, B) -> proptest::test_runner::TestCaseResult,
|
||||
) {
|
||||
let mut runner = proptest::test_runner::TestRunner::default();
|
||||
runner
|
||||
.run(&(A::default_strategy(), B::default_strategy()), |(a, b)| {
|
||||
f(a, b)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn test_unary_elementwise<Scalar, ScalarResult, Vector, VectorResult, const LANES: usize>(
|
||||
fv: impl Fn(Vector) -> VectorResult,
|
||||
fs: impl Fn(Scalar) -> ScalarResult,
|
||||
) where
|
||||
Scalar: Copy + Default + core::fmt::Debug + DefaultStrategy,
|
||||
ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
|
||||
Vector: Into<[Scalar; LANES]> + From<[Scalar; LANES]> + Copy,
|
||||
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
|
||||
{
|
||||
test_1(|x: [Scalar; LANES]| {
|
||||
let result_1: [ScalarResult; LANES] = fv(x.into()).into();
|
||||
let result_2: [ScalarResult; LANES] = {
|
||||
let mut result = [ScalarResult::default(); LANES];
|
||||
for (i, o) in x.iter().zip(result.iter_mut()) {
|
||||
*o = fs(*i);
|
||||
}
|
||||
result
|
||||
};
|
||||
crate::prop_assert_biteq!(result_1, result_2);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn test_binary_elementwise<
|
||||
Scalar1,
|
||||
Scalar2,
|
||||
ScalarResult,
|
||||
Vector1,
|
||||
Vector2,
|
||||
VectorResult,
|
||||
const LANES: usize,
|
||||
>(
|
||||
fv: impl Fn(Vector1, Vector2) -> VectorResult,
|
||||
fs: impl Fn(Scalar1, Scalar2) -> ScalarResult,
|
||||
) where
|
||||
Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
|
||||
Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
|
||||
ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
|
||||
Vector1: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
|
||||
Vector2: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
|
||||
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
|
||||
{
|
||||
test_2(|x: [Scalar1; LANES], y: [Scalar2; LANES]| {
|
||||
let result_1: [ScalarResult; LANES] = fv(x.into(), y.into()).into();
|
||||
let result_2: [ScalarResult; LANES] = {
|
||||
let mut result = [ScalarResult::default(); LANES];
|
||||
for ((i1, i2), o) in x.iter().zip(y.iter()).zip(result.iter_mut()) {
|
||||
*o = fs(*i1, *i2);
|
||||
}
|
||||
result
|
||||
};
|
||||
crate::prop_assert_biteq!(result_1, result_2);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn test_binary_scalar_rhs_elementwise<
|
||||
Scalar1,
|
||||
Scalar2,
|
||||
ScalarResult,
|
||||
Vector,
|
||||
VectorResult,
|
||||
const LANES: usize,
|
||||
>(
|
||||
fv: impl Fn(Vector, Scalar2) -> VectorResult,
|
||||
fs: impl Fn(Scalar1, Scalar2) -> ScalarResult,
|
||||
) where
|
||||
Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
|
||||
Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
|
||||
ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
|
||||
Vector: Into<[Scalar1; LANES]> + From<[Scalar1; LANES]> + Copy,
|
||||
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
|
||||
{
|
||||
test_2(|x: [Scalar1; LANES], y: Scalar2| {
|
||||
let result_1: [ScalarResult; LANES] = fv(x.into(), y).into();
|
||||
let result_2: [ScalarResult; LANES] = {
|
||||
let mut result = [ScalarResult::default(); LANES];
|
||||
for (i, o) in x.iter().zip(result.iter_mut()) {
|
||||
*o = fs(*i, y);
|
||||
}
|
||||
result
|
||||
};
|
||||
crate::prop_assert_biteq!(result_1, result_2);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
pub fn test_binary_scalar_lhs_elementwise<
|
||||
Scalar1,
|
||||
Scalar2,
|
||||
ScalarResult,
|
||||
Vector,
|
||||
VectorResult,
|
||||
const LANES: usize,
|
||||
>(
|
||||
fv: impl Fn(Scalar1, Vector) -> VectorResult,
|
||||
fs: impl Fn(Scalar1, Scalar2) -> ScalarResult,
|
||||
) where
|
||||
Scalar1: Copy + Default + core::fmt::Debug + DefaultStrategy,
|
||||
Scalar2: Copy + Default + core::fmt::Debug + DefaultStrategy,
|
||||
ScalarResult: Copy + Default + biteq::BitEq + core::fmt::Debug + DefaultStrategy,
|
||||
Vector: Into<[Scalar2; LANES]> + From<[Scalar2; LANES]> + Copy,
|
||||
VectorResult: Into<[ScalarResult; LANES]> + From<[ScalarResult; LANES]> + Copy,
|
||||
{
|
||||
test_2(|x: Scalar1, y: [Scalar2; LANES]| {
|
||||
let result_1: [ScalarResult; LANES] = fv(x, y.into()).into();
|
||||
let result_2: [ScalarResult; LANES] = {
|
||||
let mut result = [ScalarResult::default(); LANES];
|
||||
for (i, o) in y.iter().zip(result.iter_mut()) {
|
||||
*o = fs(x, *i);
|
||||
}
|
||||
result
|
||||
};
|
||||
crate::prop_assert_biteq!(result_1, result_2);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! test_lanes_impl {
|
||||
{
|
||||
fn $test:ident<const $lanes:ident: usize>() $body:tt
|
||||
|
||||
$($name:ident => $lanes_lit:literal,)*
|
||||
} => {
|
||||
mod $test {
|
||||
use super::*;
|
||||
$(
|
||||
#[test]
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
|
||||
fn $name() {
|
||||
const $lanes: usize = $lanes_lit;
|
||||
$body
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test_lanes {
|
||||
{
|
||||
$(fn $test:ident<const $lanes:ident: usize>() $body:tt)*
|
||||
} => {
|
||||
$(
|
||||
$crate::test_lanes_impl! {
|
||||
fn $test<const $lanes: usize>() $body
|
||||
|
||||
lanes_2 => 2,
|
||||
lanes_3 => 3,
|
||||
lanes_4 => 4,
|
||||
lanes_7 => 7,
|
||||
lanes_8 => 8,
|
||||
lanes_16 => 16,
|
||||
lanes_32 => 32,
|
||||
lanes_64 => 64,
|
||||
lanes_128 => 128,
|
||||
lanes_256 => 256,
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user