Merge portable-simd#232 - ./feat/typecast

Add Simd::cast
Add core_simd/tests/cast.rs
This commit is contained in:
Jubilee 2022-01-26 22:21:00 -08:00 committed by GitHub
commit 7072c2278e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 69 additions and 0 deletions

View File

@ -39,6 +39,9 @@
/// fptoui/fptosi/uitofp/sitofp
pub(crate) fn simd_cast<T, U>(x: T) -> U;
/// follows Rust's `T as U` semantics, including saturating float casts
/// which amounts to the same as `simd_cast` for many cases
pub(crate) fn simd_as<T, U>(x: T) -> U;
/// neg/fneg
pub(crate) fn simd_neg<T>(x: T) -> T;

View File

@ -75,6 +75,35 @@ pub const fn from_slice(slice: &[T]) -> Self {
Self(array)
}
/// Performs lanewise conversion of a SIMD vector's elements to another SIMD-valid type.
/// This follows the semantics of Rust's `as` conversion for casting
/// integers to unsigned integers (interpreting as the other type, so `-1` to `MAX`),
/// and from floats to integers (truncating, or saturating at the limits) for each lane,
/// or vice versa.
///
/// # Examples
/// ```
/// # #![feature(portable_simd)]
/// # #[cfg(feature = "std")] use core_simd::Simd;
/// # #[cfg(not(feature = "std"))] use core::simd::Simd;
/// let floats: Simd<f32, 4> = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]);
/// let ints = floats.cast::<i32>();
/// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0]));
///
/// // Formally equivalent, but `Simd::cast` can optimize better.
/// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32)));
///
/// // The float conversion does not round-trip.
/// let floats_again = ints.cast();
/// assert_ne!(floats, floats_again);
/// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0]));
/// ```
#[must_use]
#[inline]
pub fn cast<U: SimdElement>(self) -> Simd<U, LANES> {
unsafe { intrinsics::simd_as(self) }
}
/// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
/// If an index is out-of-bounds, the lane is instead selected from the `or` vector.
///

View File

@ -0,0 +1,37 @@
#![feature(portable_simd)]
macro_rules! cast_types {
($start:ident, $($target:ident),*) => {
mod $start {
use core_simd::simd::Simd;
type Vector<const N: usize> = Simd<$start, N>;
$(
mod $target {
use super::*;
test_helpers::test_lanes! {
fn cast_as<const N: usize>() {
test_helpers::test_unary_elementwise(
&Vector::<N>::cast::<$target>,
&|x| x as $target,
&|_| true,
)
}
}
}
)*
}
};
}
// The hypothesis is that widening conversions aren't terribly interesting.
cast_types!(f32, f64, i8, u8, usize, isize);
cast_types!(f64, f32, i8, u8, usize, isize);
cast_types!(i8, u8, f32);
cast_types!(u8, i8, f32);
cast_types!(i16, u16, i8, u8, f32);
cast_types!(u16, i16, i8, u8, f32);
cast_types!(i32, u32, i8, u8, f32, f64);
cast_types!(u32, i32, i8, u8, f32, f64);
cast_types!(i64, u64, i8, u8, isize, usize, f32, f64);
cast_types!(u64, i64, i8, u8, isize, usize, f32, f64);
cast_types!(isize, usize, i8, u8, f32, f64);
cast_types!(usize, isize, i8, u8, f32, f64);