Skip to content

Commit 7072c22

Browse files
Merge portable-simd#232 - ./feat/typecast
Add Simd::cast Add core_simd/tests/cast.rs
2 parents cad7434 + 0031b02 commit 7072c22

File tree

3 files changed

+69
-0
lines changed

3 files changed

+69
-0
lines changed

crates/core_simd/src/intrinsics.rs

+3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ extern "platform-intrinsic" {
3939

4040
/// fptoui/fptosi/uitofp/sitofp
4141
pub(crate) fn simd_cast<T, U>(x: T) -> U;
42+
/// follows Rust's `T as U` semantics, including saturating float casts
43+
/// which amounts to the same as `simd_cast` for many cases
44+
pub(crate) fn simd_as<T, U>(x: T) -> U;
4245

4346
/// neg/fneg
4447
pub(crate) fn simd_neg<T>(x: T) -> T;

crates/core_simd/src/vector.rs

+29
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,35 @@ where
7575
Self(array)
7676
}
7777

78+
/// Performs lanewise conversion of a SIMD vector's elements to another SIMD-valid type.
79+
/// This follows the semantics of Rust's `as` conversion for casting
80+
/// integers to unsigned integers (interpreting as the other type, so `-1` to `MAX`),
81+
/// and from floats to integers (truncating, or saturating at the limits) for each lane,
82+
/// or vice versa.
83+
///
84+
/// # Examples
85+
/// ```
86+
/// # #![feature(portable_simd)]
87+
/// # #[cfg(feature = "std")] use core_simd::Simd;
88+
/// # #[cfg(not(feature = "std"))] use core::simd::Simd;
89+
/// let floats: Simd<f32, 4> = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]);
90+
/// let ints = floats.cast::<i32>();
91+
/// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0]));
92+
///
93+
/// // Formally equivalent, but `Simd::cast` can optimize better.
94+
/// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32)));
95+
///
96+
/// // The float conversion does not round-trip.
97+
/// let floats_again = ints.cast();
98+
/// assert_ne!(floats, floats_again);
99+
/// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0]));
100+
/// ```
101+
#[must_use]
102+
#[inline]
103+
pub fn cast<U: SimdElement>(self) -> Simd<U, LANES> {
104+
unsafe { intrinsics::simd_as(self) }
105+
}
106+
78107
/// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
79108
/// If an index is out-of-bounds, the lane is instead selected from the `or` vector.
80109
///

crates/core_simd/tests/cast.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![feature(portable_simd)]
2+
macro_rules! cast_types {
3+
($start:ident, $($target:ident),*) => {
4+
mod $start {
5+
use core_simd::simd::Simd;
6+
type Vector<const N: usize> = Simd<$start, N>;
7+
$(
8+
mod $target {
9+
use super::*;
10+
test_helpers::test_lanes! {
11+
fn cast_as<const N: usize>() {
12+
test_helpers::test_unary_elementwise(
13+
&Vector::<N>::cast::<$target>,
14+
&|x| x as $target,
15+
&|_| true,
16+
)
17+
}
18+
}
19+
}
20+
)*
21+
}
22+
};
23+
}
24+
25+
// The hypothesis is that widening conversions aren't terribly interesting.
26+
cast_types!(f32, f64, i8, u8, usize, isize);
27+
cast_types!(f64, f32, i8, u8, usize, isize);
28+
cast_types!(i8, u8, f32);
29+
cast_types!(u8, i8, f32);
30+
cast_types!(i16, u16, i8, u8, f32);
31+
cast_types!(u16, i16, i8, u8, f32);
32+
cast_types!(i32, u32, i8, u8, f32, f64);
33+
cast_types!(u32, i32, i8, u8, f32, f64);
34+
cast_types!(i64, u64, i8, u8, isize, usize, f32, f64);
35+
cast_types!(u64, i64, i8, u8, isize, usize, f32, f64);
36+
cast_types!(isize, usize, i8, u8, f32, f64);
37+
cast_types!(usize, isize, i8, u8, f32, f64);

0 commit comments

Comments
 (0)