Skip to content

Commit

Permalink
Merge pull request rust-lang#107 from rust-lang/feat/simd-round
Browse files Browse the repository at this point in the history
Add SIMD rounding intrinsics
  • Loading branch information
workingjubilee authored Apr 26, 2021
2 parents a9a1c9d + 6ea08d8 commit 5751179
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 67 deletions.
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 }

0 comments on commit 5751179

Please sign in to comment.