Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions crates/core_simd/src/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,6 @@ extern "platform-intrinsic" {
/// neg/fneg
pub(crate) fn simd_neg<T>(x: T) -> T;

// floor
#[cfg(feature = "std")]
pub(crate) fn simd_floor<T>(x: T) -> T;

// ceil
#[cfg(feature = "std")]
pub(crate) fn simd_ceil<T>(x: T) -> T;

/// fabs
pub(crate) fn simd_fabs<T>(x: T) -> T;

Expand Down Expand Up @@ -85,3 +77,23 @@ extern "platform-intrinsic" {
pub(crate) fn simd_reduce_or<T, U>(x: T) -> U;
pub(crate) fn simd_reduce_xor<T, U>(x: T) -> U;
}

#[cfg(feature = "std")]
mod std {
extern "platform-intrinsic" {
// ceil
pub(crate) fn simd_ceil<T>(x: T) -> T;

// floor
pub(crate) fn simd_floor<T>(x: T) -> T;

// round
pub(crate) fn simd_round<T>(x: T) -> T;

// trunc
pub(crate) fn simd_trunc<T>(x: T) -> T;
}
}

#[cfg(feature = "std")]
pub(crate) use crate::intrinsics::std::*;
32 changes: 26 additions & 6 deletions crates/core_simd/src/round.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,44 @@ macro_rules! implement {
{
$type:ident, $int_type:ident
} => {
#[cfg(feature = "std")]
impl<const LANES: usize> crate::$type<LANES>
where
Self: crate::LanesAtMost32,
{
/// Returns the largest integer less than or equal to each lane.
#[cfg(feature = "std")]
/// Returns the smallest integer greater than or equal to each lane.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
pub fn ceil(self) -> Self {
unsafe { crate::intrinsics::simd_ceil(self) }
}

/// Returns the largest integer value less than or equal to each lane.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
pub fn floor(self) -> Self {
unsafe { crate::intrinsics::simd_floor(self) }
}

/// Returns the smallest integer greater than or equal to each lane.
#[cfg(feature = "std")]
/// Rounds to the nearest integer value. Ties round toward zero.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
pub fn ceil(self) -> Self {
unsafe { crate::intrinsics::simd_ceil(self) }
pub fn round(self) -> Self {
unsafe { crate::intrinsics::simd_round(self) }
}

/// Returns the floating point's integer value, with its fractional part removed.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
pub fn trunc(self) -> Self {
unsafe { crate::intrinsics::simd_trunc(self) }
}

/// Returns the floating point's fractional value, with its integer part removed.
#[must_use = "method returns a new vector and does not mutate the original value"]
#[inline]
pub fn fract(self) -> Self {
self - self.trunc()
}
}

Expand Down
53 changes: 0 additions & 53 deletions crates/core_simd/tests/ops_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ macro_rules! impl_float_tests {
mod $scalar {
type Vector<const LANES: usize> = core_simd::$vector<LANES>;
type Scalar = $scalar;
type IntScalar = $int_scalar;

impl_unary_op_test!(Vector<LANES>, Scalar, Neg::neg);
impl_binary_op_test!(Vector<LANES>, Scalar, Add::add, AddAssign::add_assign);
Expand All @@ -362,25 +361,6 @@ macro_rules! impl_float_tests {
impl_binary_op_test!(Vector<LANES>, Scalar, Div::div, DivAssign::div_assign);
impl_binary_op_test!(Vector<LANES>, Scalar, Rem::rem, RemAssign::rem_assign);

#[cfg(feature = "std")]
test_helpers::test_lanes! {
fn ceil<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::ceil,
&Scalar::ceil,
&|_| true,
)
}

fn floor<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::floor,
&Scalar::floor,
&|_| true,
)
}
}

test_helpers::test_lanes! {
fn is_sign_positive<const LANES: usize>() {
test_helpers::test_unary_mask_elementwise(
Expand Down Expand Up @@ -446,39 +426,6 @@ macro_rules! impl_float_tests {
)
}

fn round_from_int<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::round_from_int,
&|x| x as Scalar,
&|_| true,
)
}

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();
}

fn horizontal_sum<const LANES: usize>() {
test_helpers::test_1(&|x| {
test_helpers::prop_assert_biteq! (
Expand Down
90 changes: 90 additions & 0 deletions crates/core_simd/tests/round.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
macro_rules! float_rounding_test {
{ $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;

#[cfg(feature = "std")]
test_helpers::test_lanes! {
fn ceil<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::ceil,
&Scalar::ceil,
&|_| true,
)
}

fn floor<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::floor,
&Scalar::floor,
&|_| true,
)
}

fn round<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::round,
&Scalar::round,
&|_| true,
)
}

fn trunc<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::trunc,
&Scalar::trunc,
&|_| true,
)
}

fn fract<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::fract,
&Scalar::fract,
&|_| true,
)
}
}

test_helpers::test_lanes! {
fn from_int<const LANES: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<LANES>::round_from_int,
&|x| x as Scalar,
&|_| true,
)
}

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();
}
}
}
}
}

float_rounding_test! { SimdF32, f32, i32 }
float_rounding_test! { SimdF64, f64, i64 }