Skip to content

Commit 5751179

Browse files
Merge pull request rust-lang#107 from rust-lang/feat/simd-round
Add SIMD rounding intrinsics
2 parents a9a1c9d + 6ea08d8 commit 5751179

File tree

4 files changed

+136
-67
lines changed

4 files changed

+136
-67
lines changed

crates/core_simd/src/intrinsics.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,6 @@ extern "platform-intrinsic" {
4343
/// neg/fneg
4444
pub(crate) fn simd_neg<T>(x: T) -> T;
4545

46-
// floor
47-
#[cfg(feature = "std")]
48-
pub(crate) fn simd_floor<T>(x: T) -> T;
49-
50-
// ceil
51-
#[cfg(feature = "std")]
52-
pub(crate) fn simd_ceil<T>(x: T) -> T;
53-
5446
/// fabs
5547
pub(crate) fn simd_fabs<T>(x: T) -> T;
5648

@@ -85,3 +77,23 @@ extern "platform-intrinsic" {
8577
pub(crate) fn simd_reduce_or<T, U>(x: T) -> U;
8678
pub(crate) fn simd_reduce_xor<T, U>(x: T) -> U;
8779
}
80+
81+
#[cfg(feature = "std")]
82+
mod std {
83+
extern "platform-intrinsic" {
84+
// ceil
85+
pub(crate) fn simd_ceil<T>(x: T) -> T;
86+
87+
// floor
88+
pub(crate) fn simd_floor<T>(x: T) -> T;
89+
90+
// round
91+
pub(crate) fn simd_round<T>(x: T) -> T;
92+
93+
// trunc
94+
pub(crate) fn simd_trunc<T>(x: T) -> T;
95+
}
96+
}
97+
98+
#[cfg(feature = "std")]
99+
pub(crate) use crate::intrinsics::std::*;

crates/core_simd/src/round.rs

+26-6
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,44 @@ macro_rules! implement {
22
{
33
$type:ident, $int_type:ident
44
} => {
5+
#[cfg(feature = "std")]
56
impl<const LANES: usize> crate::$type<LANES>
67
where
78
Self: crate::LanesAtMost32,
89
{
9-
/// Returns the largest integer less than or equal to each lane.
10-
#[cfg(feature = "std")]
10+
/// Returns the smallest integer greater than or equal to each lane.
11+
#[must_use = "method returns a new vector and does not mutate the original value"]
12+
#[inline]
13+
pub fn ceil(self) -> Self {
14+
unsafe { crate::intrinsics::simd_ceil(self) }
15+
}
16+
17+
/// Returns the largest integer value less than or equal to each lane.
1118
#[must_use = "method returns a new vector and does not mutate the original value"]
1219
#[inline]
1320
pub fn floor(self) -> Self {
1421
unsafe { crate::intrinsics::simd_floor(self) }
1522
}
1623

17-
/// Returns the smallest integer greater than or equal to each lane.
18-
#[cfg(feature = "std")]
24+
/// Rounds to the nearest integer value. Ties round toward zero.
1925
#[must_use = "method returns a new vector and does not mutate the original value"]
2026
#[inline]
21-
pub fn ceil(self) -> Self {
22-
unsafe { crate::intrinsics::simd_ceil(self) }
27+
pub fn round(self) -> Self {
28+
unsafe { crate::intrinsics::simd_round(self) }
29+
}
30+
31+
/// Returns the floating point's integer value, with its fractional part removed.
32+
#[must_use = "method returns a new vector and does not mutate the original value"]
33+
#[inline]
34+
pub fn trunc(self) -> Self {
35+
unsafe { crate::intrinsics::simd_trunc(self) }
36+
}
37+
38+
/// Returns the floating point's fractional value, with its integer part removed.
39+
#[must_use = "method returns a new vector and does not mutate the original value"]
40+
#[inline]
41+
pub fn fract(self) -> Self {
42+
self - self.trunc()
2343
}
2444
}
2545

crates/core_simd/tests/ops_macros.rs

-53
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,6 @@ macro_rules! impl_float_tests {
353353
mod $scalar {
354354
type Vector<const LANES: usize> = core_simd::$vector<LANES>;
355355
type Scalar = $scalar;
356-
type IntScalar = $int_scalar;
357356

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

365-
#[cfg(feature = "std")]
366-
test_helpers::test_lanes! {
367-
fn ceil<const LANES: usize>() {
368-
test_helpers::test_unary_elementwise(
369-
&Vector::<LANES>::ceil,
370-
&Scalar::ceil,
371-
&|_| true,
372-
)
373-
}
374-
375-
fn floor<const LANES: usize>() {
376-
test_helpers::test_unary_elementwise(
377-
&Vector::<LANES>::floor,
378-
&Scalar::floor,
379-
&|_| true,
380-
)
381-
}
382-
}
383-
384364
test_helpers::test_lanes! {
385365
fn is_sign_positive<const LANES: usize>() {
386366
test_helpers::test_unary_mask_elementwise(
@@ -446,39 +426,6 @@ macro_rules! impl_float_tests {
446426
)
447427
}
448428

449-
fn round_from_int<const LANES: usize>() {
450-
test_helpers::test_unary_elementwise(
451-
&Vector::<LANES>::round_from_int,
452-
&|x| x as Scalar,
453-
&|_| true,
454-
)
455-
}
456-
457-
fn to_int_unchecked<const LANES: usize>() {
458-
// The maximum integer that can be represented by the equivalently sized float has
459-
// all of the mantissa digits set to 1, pushed up to the MSB.
460-
const ALL_MANTISSA_BITS: IntScalar = ((1 << <Scalar>::MANTISSA_DIGITS) - 1);
461-
const MAX_REPRESENTABLE_VALUE: Scalar =
462-
(ALL_MANTISSA_BITS << (core::mem::size_of::<Scalar>() * 8 - <Scalar>::MANTISSA_DIGITS as usize - 1)) as Scalar;
463-
464-
let mut runner = proptest::test_runner::TestRunner::default();
465-
runner.run(
466-
&test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE),
467-
|x| {
468-
let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() };
469-
let result_2 = {
470-
let mut result = [0; LANES];
471-
for (i, o) in x.iter().zip(result.iter_mut()) {
472-
*o = unsafe { i.to_int_unchecked() };
473-
}
474-
result
475-
};
476-
test_helpers::prop_assert_biteq!(result_1, result_2);
477-
Ok(())
478-
},
479-
).unwrap();
480-
}
481-
482429
fn horizontal_sum<const LANES: usize>() {
483430
test_helpers::test_1(&|x| {
484431
test_helpers::prop_assert_biteq! (

crates/core_simd/tests/round.rs

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
macro_rules! float_rounding_test {
2+
{ $vector:ident, $scalar:tt, $int_scalar:tt } => {
3+
mod $scalar {
4+
type Vector<const LANES: usize> = core_simd::$vector<LANES>;
5+
type Scalar = $scalar;
6+
type IntScalar = $int_scalar;
7+
8+
#[cfg(feature = "std")]
9+
test_helpers::test_lanes! {
10+
fn ceil<const LANES: usize>() {
11+
test_helpers::test_unary_elementwise(
12+
&Vector::<LANES>::ceil,
13+
&Scalar::ceil,
14+
&|_| true,
15+
)
16+
}
17+
18+
fn floor<const LANES: usize>() {
19+
test_helpers::test_unary_elementwise(
20+
&Vector::<LANES>::floor,
21+
&Scalar::floor,
22+
&|_| true,
23+
)
24+
}
25+
26+
fn round<const LANES: usize>() {
27+
test_helpers::test_unary_elementwise(
28+
&Vector::<LANES>::round,
29+
&Scalar::round,
30+
&|_| true,
31+
)
32+
}
33+
34+
fn trunc<const LANES: usize>() {
35+
test_helpers::test_unary_elementwise(
36+
&Vector::<LANES>::trunc,
37+
&Scalar::trunc,
38+
&|_| true,
39+
)
40+
}
41+
42+
fn fract<const LANES: usize>() {
43+
test_helpers::test_unary_elementwise(
44+
&Vector::<LANES>::fract,
45+
&Scalar::fract,
46+
&|_| true,
47+
)
48+
}
49+
}
50+
51+
test_helpers::test_lanes! {
52+
fn from_int<const LANES: usize>() {
53+
test_helpers::test_unary_elementwise(
54+
&Vector::<LANES>::round_from_int,
55+
&|x| x as Scalar,
56+
&|_| true,
57+
)
58+
}
59+
60+
fn to_int_unchecked<const LANES: usize>() {
61+
// The maximum integer that can be represented by the equivalently sized float has
62+
// all of the mantissa digits set to 1, pushed up to the MSB.
63+
const ALL_MANTISSA_BITS: IntScalar = ((1 << <Scalar>::MANTISSA_DIGITS) - 1);
64+
const MAX_REPRESENTABLE_VALUE: Scalar =
65+
(ALL_MANTISSA_BITS << (core::mem::size_of::<Scalar>() * 8 - <Scalar>::MANTISSA_DIGITS as usize - 1)) as Scalar;
66+
67+
let mut runner = proptest::test_runner::TestRunner::default();
68+
runner.run(
69+
&test_helpers::array::UniformArrayStrategy::new(-MAX_REPRESENTABLE_VALUE..MAX_REPRESENTABLE_VALUE),
70+
|x| {
71+
let result_1 = unsafe { Vector::from_array(x).to_int_unchecked().to_array() };
72+
let result_2 = {
73+
let mut result = [0; LANES];
74+
for (i, o) in x.iter().zip(result.iter_mut()) {
75+
*o = unsafe { i.to_int_unchecked() };
76+
}
77+
result
78+
};
79+
test_helpers::prop_assert_biteq!(result_1, result_2);
80+
Ok(())
81+
},
82+
).unwrap();
83+
}
84+
}
85+
}
86+
}
87+
}
88+
89+
float_rounding_test! { SimdF32, f32, i32 }
90+
float_rounding_test! { SimdF64, f64, i64 }

0 commit comments

Comments
 (0)