diff --git a/fearless_simd_tests/tests/mod.rs b/fearless_simd_tests/tests/mod.rs index c6575e800..24c76c095 100644 --- a/fearless_simd_tests/tests/mod.rs +++ b/fearless_simd_tests/tests/mod.rs @@ -63,7 +63,7 @@ fn supports_highest_level() { #[simd_test] #[ignore] -fn test_f32_to_i32_exhaustive(simd: S) { +fn test_f32_to_i32_precise_exhaustive(simd: S) { // The vectorize call doesn't affect the outcome of the test, but does make it complete far more quickly #[expect( clippy::cast_possible_truncation, @@ -74,11 +74,35 @@ fn test_f32_to_i32_exhaustive(simd: S) { || { for i in (0..u32::MAX).step_by(4) { let floats = f32x4::from_fn(simd, |n| f32::from_bits(n as u32 + i)); - let ints = floats.to_int::>(); + let ints = floats.to_int_precise::>(); let ints_ref = (*floats).map(|f| f as i32); assert_eq!( *ints, ints_ref, - "f32x4::to_int::>() returns the same results as Rust's `as i32`" + "f32x4::to_int_precise::>() returns the same results as Rust's `as i32`" + ); + } + }, + ); +} + +#[simd_test] +#[ignore] +fn test_f32_to_u32_precise_exhaustive(simd: S) { + // The vectorize call doesn't affect the outcome of the test, but does make it complete far more quickly + #[expect( + clippy::cast_possible_truncation, + reason = "that's the exact behavior we're testing" + )] + simd.vectorize( + #[inline(always)] + || { + for i in (0..u32::MAX).step_by(4) { + let floats = f32x4::from_fn(simd, |n| f32::from_bits(n as u32 + i)); + let ints = floats.to_int_precise::>(); + let ints_ref = (*floats).map(|f| f as u32); + assert_eq!( + *ints, ints_ref, + "f32x4::to_int_precise::>() returns the same results as Rust's `as u32`" ); } }, @@ -98,11 +122,49 @@ fn test_f32_to_u32_exhaustive(simd: S) { || { for i in (0..u32::MAX).step_by(4) { let floats = f32x4::from_fn(simd, |n| f32::from_bits(n as u32 + i)); + // If the value is out of range of u32 because f32 cannot represent it exactly, skip the value + // The out-of-range semantics are explicitly implementation-defined in the non-precise version. + if ! (*floats).iter().all(|val| !val.is_nan() && *val > u32::MIN as f32 && *val < u32::MAX as f32) { + continue; + } let ints = floats.to_int::>(); let ints_ref = (*floats).map(|f| f as u32); assert_eq!( *ints, ints_ref, - "f32x4::to_int::>() returns the same results as Rust's `as u32`" + "f32x4::to_int::>() returns the same results as Rust's `as u32` (input: {:?})", floats.as_slice() + ); + } + }, + ); +} + +#[simd_test] +#[ignore] +fn test_f32_to_i32_exhaustive(simd: S) { + // The vectorize call doesn't affect the outcome of the test, but does make it complete far more quickly + #[expect( + clippy::cast_possible_truncation, + reason = "that's the exact behavior we're testing" + )] + simd.vectorize( + #[inline(always)] + || { + for i in (0..u32::MAX).step_by(4) { + let floats = f32x4::from_fn(simd, |n| f32::from_bits(n as u32 + i)); + // If the value is out of range of i32 because f32 cannot represent it exactly, skip the value + // The out-of-range semantics are explicitly implementation-defined in the non-precise version. + if !(*floats) + .iter() + .all(|val| !val.is_nan() && *val > i32::MIN as f32 && *val < i32::MAX as f32) + { + continue; + } + let ints = floats.to_int::>(); + let ints_ref = (*floats).map(|f| f as i32); + assert_eq!( + *ints, ints_ref, + "f32x4::to_int::>() returns the same results as Rust's `as i32` (input: {:?})", + floats.as_slice() ); } },