Add float trait, and seal traits.
This commit is contained in:
parent
376957ad8c
commit
04be48ff97
8 changed files with 368 additions and 345 deletions
|
|
@ -1,5 +1,11 @@
|
|||
mod float;
|
||||
mod int;
|
||||
mod uint;
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
pub use float::*;
|
||||
pub use int::*;
|
||||
pub use uint::*;
|
||||
|
|
|
|||
344
crates/core_simd/src/elements/float.rs
Normal file
344
crates/core_simd/src/elements/float.rs
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
use super::sealed::Sealed;
|
||||
use crate::simd::{
|
||||
intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialEq, SimdPartialOrd,
|
||||
SupportedLaneCount,
|
||||
};
|
||||
|
||||
/// Operations on SIMD vectors of floats.
|
||||
pub trait SimdFloat: Sized + Sealed {
|
||||
/// Mask type used for manipulating this SIMD vector type.
|
||||
type Mask;
|
||||
|
||||
/// Scalar type contained by this SIMD vector type.
|
||||
type Scalar;
|
||||
|
||||
/// Bit representation of this SIMD vector type.
|
||||
type Bits;
|
||||
|
||||
/// Raw transmutation to an unsigned integer vector type with the
|
||||
/// same size and number of lanes.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn to_bits(self) -> Self::Bits;
|
||||
|
||||
/// Raw transmutation from an unsigned integer vector type with the
|
||||
/// same size and number of lanes.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn from_bits(bits: Self::Bits) -> Self;
|
||||
|
||||
/// Produces a vector where every lane has the absolute value of the
|
||||
/// equivalently-indexed lane in `self`.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn abs(self) -> Self;
|
||||
|
||||
/// Takes the reciprocal (inverse) of each lane, `1/x`.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn recip(self) -> Self;
|
||||
|
||||
/// Converts each lane from radians to degrees.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn to_degrees(self) -> Self;
|
||||
|
||||
/// Converts each lane from degrees to radians.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn to_radians(self) -> Self;
|
||||
|
||||
/// Returns true for each lane if it has a positive sign, including
|
||||
/// `+0.0`, `NaN`s with positive sign bit and positive infinity.
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_sign_positive(self) -> Self::Mask;
|
||||
|
||||
/// Returns true for each lane if it has a negative sign, including
|
||||
/// `-0.0`, `NaN`s with negative sign bit and negative infinity.
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_sign_negative(self) -> Self::Mask;
|
||||
|
||||
/// Returns true for each lane if its value is `NaN`.
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_nan(self) -> Self::Mask;
|
||||
|
||||
/// Returns true for each lane if its value is positive infinity or negative infinity.
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_infinite(self) -> Self::Mask;
|
||||
|
||||
/// Returns true for each lane if its value is neither infinite nor `NaN`.
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_finite(self) -> Self::Mask;
|
||||
|
||||
/// Returns true for each lane if its value is subnormal.
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_subnormal(self) -> Self::Mask;
|
||||
|
||||
/// Returns true for each lane if its value is neither zero, infinite,
|
||||
/// subnormal, nor `NaN`.
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_normal(self) -> Self::Mask;
|
||||
|
||||
/// Replaces each lane with a number that represents its sign.
|
||||
///
|
||||
/// * `1.0` if the number is positive, `+0.0`, or `INFINITY`
|
||||
/// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY`
|
||||
/// * `NAN` if the number is `NAN`
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn signum(self) -> Self;
|
||||
|
||||
/// Returns each lane with the magnitude of `self` and the sign of `sign`.
|
||||
///
|
||||
/// If any lane is a `NAN`, then a `NAN` with the sign of `sign` is returned.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn copysign(self, sign: Self) -> Self;
|
||||
|
||||
/// Returns the minimum of each lane.
|
||||
///
|
||||
/// If one of the values is `NAN`, then the other value is returned.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn simd_min(self, other: Self) -> Self;
|
||||
|
||||
/// Returns the maximum of each lane.
|
||||
///
|
||||
/// If one of the values is `NAN`, then the other value is returned.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn simd_max(self, other: Self) -> Self;
|
||||
|
||||
/// Restrict each lane to a certain interval unless it is NaN.
|
||||
///
|
||||
/// For each lane in `self`, returns the corresponding lane in `max` if the lane is
|
||||
/// greater than `max`, and the corresponding lane in `min` if the lane is less
|
||||
/// than `min`. Otherwise returns the lane in `self`.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn simd_clamp(self, min: Self, max: Self) -> Self;
|
||||
|
||||
/// Returns the sum of the lanes of the vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::f32x2;
|
||||
/// let v = f32x2::from_array([1., 2.]);
|
||||
/// assert_eq!(v.reduce_sum(), 3.);
|
||||
/// ```
|
||||
fn reduce_sum(self) -> Self::Scalar;
|
||||
|
||||
/// Reducing multiply. Returns the product of the lanes of the vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::f32x2;
|
||||
/// let v = f32x2::from_array([3., 4.]);
|
||||
/// assert_eq!(v.reduce_product(), 12.);
|
||||
/// ```
|
||||
fn reduce_product(self) -> Self::Scalar;
|
||||
|
||||
/// Returns the maximum lane in the vector.
|
||||
///
|
||||
/// Returns values based on equality, so a vector containing both `0.` and `-0.` may
|
||||
/// return either.
|
||||
///
|
||||
/// This function will not return `NaN` unless all lanes are `NaN`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::f32x2;
|
||||
/// let v = f32x2::from_array([1., 2.]);
|
||||
/// assert_eq!(v.reduce_max(), 2.);
|
||||
///
|
||||
/// // NaN values are skipped...
|
||||
/// let v = f32x2::from_array([1., f32::NAN]);
|
||||
/// assert_eq!(v.reduce_max(), 1.);
|
||||
///
|
||||
/// // ...unless all values are NaN
|
||||
/// let v = f32x2::from_array([f32::NAN, f32::NAN]);
|
||||
/// assert!(v.reduce_max().is_nan());
|
||||
/// ```
|
||||
fn reduce_max(self) -> Self::Scalar;
|
||||
|
||||
/// Returns the minimum lane in the vector.
|
||||
///
|
||||
/// Returns values based on equality, so a vector containing both `0.` and `-0.` may
|
||||
/// return either.
|
||||
///
|
||||
/// This function will not return `NaN` unless all lanes are `NaN`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::f32x2;
|
||||
/// let v = f32x2::from_array([3., 7.]);
|
||||
/// assert_eq!(v.reduce_min(), 3.);
|
||||
///
|
||||
/// // NaN values are skipped...
|
||||
/// let v = f32x2::from_array([1., f32::NAN]);
|
||||
/// assert_eq!(v.reduce_min(), 1.);
|
||||
///
|
||||
/// // ...unless all values are NaN
|
||||
/// let v = f32x2::from_array([f32::NAN, f32::NAN]);
|
||||
/// assert!(v.reduce_min().is_nan());
|
||||
/// ```
|
||||
fn reduce_min(self) -> Self::Scalar;
|
||||
}
|
||||
|
||||
macro_rules! impl_trait {
|
||||
{ $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => {
|
||||
$(
|
||||
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
}
|
||||
|
||||
impl<const LANES: usize> SimdFloat for Simd<$ty, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
type Mask = Mask<<$mask_ty as SimdElement>::Mask, LANES>;
|
||||
type Scalar = $ty;
|
||||
type Bits = Simd<$bits_ty, LANES>;
|
||||
|
||||
#[inline]
|
||||
fn to_bits(self) -> Simd<$bits_ty, LANES> {
|
||||
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Self::Bits>());
|
||||
unsafe { core::mem::transmute_copy(&self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self {
|
||||
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Self::Bits>());
|
||||
unsafe { core::mem::transmute_copy(&bits) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn abs(self) -> Self {
|
||||
unsafe { intrinsics::simd_fabs(self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn recip(self) -> Self {
|
||||
Self::splat(1.0) / self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_degrees(self) -> Self {
|
||||
// to_degrees uses a special constant for better precision, so extract that constant
|
||||
self * Self::splat(Self::Scalar::to_degrees(1.))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_radians(self) -> Self {
|
||||
self * Self::splat(Self::Scalar::to_radians(1.))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_sign_positive(self) -> Self::Mask {
|
||||
!self.is_sign_negative()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_sign_negative(self) -> Self::Mask {
|
||||
let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
|
||||
sign_bits.simd_gt(Simd::splat(0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_nan(self) -> Self::Mask {
|
||||
self.simd_ne(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_infinite(self) -> Self::Mask {
|
||||
self.abs().simd_eq(Self::splat(Self::Scalar::INFINITY))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_finite(self) -> Self::Mask {
|
||||
self.abs().simd_lt(Self::splat(Self::Scalar::INFINITY))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_subnormal(self) -> Self::Mask {
|
||||
self.abs().simd_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(Self::Scalar::INFINITY).to_bits()).simd_eq(Simd::splat(0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
fn is_normal(self) -> Self::Mask {
|
||||
!(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn signum(self) -> Self {
|
||||
self.is_nan().select(Self::splat(Self::Scalar::NAN), Self::splat(1.0).copysign(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn copysign(self, sign: Self) -> Self {
|
||||
let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
|
||||
let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
|
||||
Self::from_bits(sign_bit | magnitude)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simd_min(self, other: Self) -> Self {
|
||||
unsafe { intrinsics::simd_fmin(self, other) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simd_max(self, other: Self) -> Self {
|
||||
unsafe { intrinsics::simd_fmax(self, other) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simd_clamp(self, min: Self, max: Self) -> Self {
|
||||
assert!(
|
||||
min.simd_le(max).all(),
|
||||
"each lane in `min` must be less than or equal to the corresponding lane in `max`",
|
||||
);
|
||||
let mut x = self;
|
||||
x = x.simd_lt(min).select(min, x);
|
||||
x = x.simd_gt(max).select(max, x);
|
||||
x
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reduce_sum(self) -> Self::Scalar {
|
||||
// LLVM sum is inaccurate on i586
|
||||
if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
|
||||
self.as_array().iter().sum()
|
||||
} else {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { intrinsics::simd_reduce_add_ordered(self, 0.) }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reduce_product(self) -> Self::Scalar {
|
||||
// LLVM product is inaccurate on i586
|
||||
if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
|
||||
self.as_array().iter().product()
|
||||
} else {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { intrinsics::simd_reduce_mul_ordered(self, 1.) }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reduce_max(self) -> Self::Scalar {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { intrinsics::simd_reduce_max(self) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reduce_min(self) -> Self::Scalar {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { intrinsics::simd_reduce_min(self) }
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } }
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
use super::sealed::Sealed;
|
||||
use crate::simd::{
|
||||
intrinsics, LaneCount, Mask, Simd, SimdElement, SimdPartialOrd, SupportedLaneCount,
|
||||
};
|
||||
|
||||
/// Operations on SIMD vectors of signed integers.
|
||||
pub trait SimdInt: Sized {
|
||||
pub trait SimdInt: Sized + Sealed {
|
||||
/// Mask type used for manipulating this SIMD vector type.
|
||||
type Mask;
|
||||
|
||||
|
|
@ -167,6 +168,12 @@ pub trait SimdInt: Sized {
|
|||
macro_rules! impl_trait {
|
||||
{ $($ty:ty),* } => {
|
||||
$(
|
||||
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
}
|
||||
|
||||
impl<const LANES: usize> SimdInt for Simd<$ty, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use super::sealed::Sealed;
|
||||
use crate::simd::{intrinsics, LaneCount, Simd, SupportedLaneCount};
|
||||
|
||||
/// Operations on SIMD vectors of unsigned integers.
|
||||
pub trait SimdUint: Sized {
|
||||
pub trait SimdUint: Sized + Sealed {
|
||||
/// Scalar type contained by this SIMD vector type.
|
||||
type Scalar;
|
||||
|
||||
|
|
@ -61,6 +62,12 @@ pub trait SimdUint: Sized {
|
|||
macro_rules! impl_trait {
|
||||
{ $($ty:ty),* } => {
|
||||
$(
|
||||
impl<const LANES: usize> Sealed for Simd<$ty, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
}
|
||||
|
||||
impl<const LANES: usize> SimdUint for Simd<$ty, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
#[macro_use]
|
||||
mod reduction;
|
||||
|
||||
#[macro_use]
|
||||
mod swizzle;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
use crate::simd::intrinsics::{
|
||||
simd_reduce_add_ordered, simd_reduce_max, simd_reduce_min, simd_reduce_mul_ordered,
|
||||
};
|
||||
use crate::simd::{LaneCount, Simd, SupportedLaneCount};
|
||||
|
||||
macro_rules! impl_float_reductions {
|
||||
{ $scalar:ty } => {
|
||||
impl<const LANES: usize> Simd<$scalar, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
|
||||
/// Reducing add. Returns the sum of the lanes of the vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::Simd;
|
||||
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., 2.]);")]
|
||||
/// assert_eq!(v.reduce_sum(), 3.);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn reduce_sum(self) -> $scalar {
|
||||
// LLVM sum is inaccurate on i586
|
||||
if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
|
||||
self.as_array().iter().sum()
|
||||
} else {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { simd_reduce_add_ordered(self, 0.) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Reducing multiply. Returns the product of the lanes of the vector.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::Simd;
|
||||
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([3., 4.]);")]
|
||||
/// assert_eq!(v.reduce_product(), 12.);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn reduce_product(self) -> $scalar {
|
||||
// LLVM product is inaccurate on i586
|
||||
if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
|
||||
self.as_array().iter().product()
|
||||
} else {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { simd_reduce_mul_ordered(self, 1.) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Reducing maximum. Returns the maximum lane in the vector.
|
||||
///
|
||||
/// Returns values based on equality, so a vector containing both `0.` and `-0.` may
|
||||
/// return either.
|
||||
///
|
||||
/// This function will not return `NaN` unless all lanes are `NaN`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::Simd;
|
||||
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., 2.]);")]
|
||||
/// assert_eq!(v.reduce_max(), 2.);
|
||||
///
|
||||
/// // NaN values are skipped...
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., ", stringify!($scalar), "::NAN]);")]
|
||||
/// assert_eq!(v.reduce_max(), 1.);
|
||||
///
|
||||
/// // ...unless all values are NaN
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([",
|
||||
stringify!($scalar), "::NAN, ",
|
||||
stringify!($scalar), "::NAN]);"
|
||||
)]
|
||||
/// assert!(v.reduce_max().is_nan());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn reduce_max(self) -> $scalar {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { simd_reduce_max(self) }
|
||||
}
|
||||
|
||||
/// Reducing minimum. Returns the minimum lane in the vector.
|
||||
///
|
||||
/// Returns values based on equality, so a vector containing both `0.` and `-0.` may
|
||||
/// return either.
|
||||
///
|
||||
/// This function will not return `NaN` unless all lanes are `NaN`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # #![feature(portable_simd)]
|
||||
/// # use core::simd::Simd;
|
||||
#[doc = concat!("# use core::simd::", stringify!($scalar), "x2;")]
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([3., 7.]);")]
|
||||
/// assert_eq!(v.reduce_min(), 3.);
|
||||
///
|
||||
/// // NaN values are skipped...
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([1., ", stringify!($scalar), "::NAN]);")]
|
||||
/// assert_eq!(v.reduce_min(), 1.);
|
||||
///
|
||||
/// // ...unless all values are NaN
|
||||
#[doc = concat!("let v = ", stringify!($scalar), "x2::from_array([",
|
||||
stringify!($scalar), "::NAN, ",
|
||||
stringify!($scalar), "::NAN]);"
|
||||
)]
|
||||
/// assert!(v.reduce_min().is_nan());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn reduce_min(self) -> $scalar {
|
||||
// Safety: `self` is a float vector
|
||||
unsafe { simd_reduce_min(self) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_float_reductions! { f32 }
|
||||
impl_float_reductions! { f64 }
|
||||
|
|
@ -1,145 +1,6 @@
|
|||
#![allow(non_camel_case_types)]
|
||||
|
||||
use crate::simd::intrinsics;
|
||||
use crate::simd::{LaneCount, Mask, Simd, SimdPartialEq, SimdPartialOrd, SupportedLaneCount};
|
||||
|
||||
/// Implements inherent methods for a float vector containing multiple
|
||||
/// `$lanes` of float `$type`, which uses `$bits_ty` as its binary
|
||||
/// representation.
|
||||
macro_rules! impl_float_vector {
|
||||
{ $type:ty, $bits_ty:ty, $mask_ty:ty } => {
|
||||
impl<const LANES: usize> Simd<$type, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
/// Raw transmutation to an unsigned integer vector type with the
|
||||
/// same size and number of lanes.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn to_bits(self) -> Simd<$bits_ty, LANES> {
|
||||
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
|
||||
unsafe { core::mem::transmute_copy(&self) }
|
||||
}
|
||||
|
||||
/// Raw transmutation from an unsigned integer vector type with the
|
||||
/// same size and number of lanes.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self {
|
||||
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
|
||||
unsafe { core::mem::transmute_copy(&bits) }
|
||||
}
|
||||
|
||||
/// Produces a vector where every lane has the absolute value of the
|
||||
/// equivalently-indexed lane in `self`.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn abs(self) -> Self {
|
||||
unsafe { intrinsics::simd_fabs(self) }
|
||||
}
|
||||
|
||||
/// Takes the reciprocal (inverse) of each lane, `1/x`.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn recip(self) -> Self {
|
||||
Self::splat(1.0) / self
|
||||
}
|
||||
|
||||
/// Converts each lane from radians to degrees.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn to_degrees(self) -> Self {
|
||||
// to_degrees uses a special constant for better precision, so extract that constant
|
||||
self * Self::splat(<$type>::to_degrees(1.))
|
||||
}
|
||||
|
||||
/// Converts each lane from degrees to radians.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn to_radians(self) -> Self {
|
||||
self * Self::splat(<$type>::to_radians(1.))
|
||||
}
|
||||
|
||||
/// Returns true for each lane if it has a positive sign, including
|
||||
/// `+0.0`, `NaN`s with positive sign bit and positive infinity.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
pub fn is_sign_positive(self) -> Mask<$mask_ty, LANES> {
|
||||
!self.is_sign_negative()
|
||||
}
|
||||
|
||||
/// Returns true for each lane if it has a negative sign, including
|
||||
/// `-0.0`, `NaN`s with negative sign bit and negative infinity.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
pub fn is_sign_negative(self) -> Mask<$mask_ty, LANES> {
|
||||
let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
|
||||
sign_bits.simd_gt(Simd::splat(0))
|
||||
}
|
||||
|
||||
/// Returns true for each lane if its value is `NaN`.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
pub fn is_nan(self) -> Mask<$mask_ty, LANES> {
|
||||
self.simd_ne(self)
|
||||
}
|
||||
|
||||
/// Returns true for each lane if its value is positive infinity or negative infinity.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
pub fn is_infinite(self) -> Mask<$mask_ty, LANES> {
|
||||
self.abs().simd_eq(Self::splat(<$type>::INFINITY))
|
||||
}
|
||||
|
||||
/// Returns true for each lane if its value is neither infinite nor `NaN`.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
pub fn is_finite(self) -> Mask<$mask_ty, LANES> {
|
||||
self.abs().simd_lt(Self::splat(<$type>::INFINITY))
|
||||
}
|
||||
|
||||
/// Returns true for each lane if its value is subnormal.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
pub fn is_subnormal(self) -> Mask<$mask_ty, LANES> {
|
||||
self.abs().simd_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(<$type>::INFINITY).to_bits()).simd_eq(Simd::splat(0))
|
||||
}
|
||||
|
||||
/// Returns true for each lane if its value is neither zero, infinite,
|
||||
/// subnormal, nor `NaN`.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new mask and does not mutate the original value"]
|
||||
pub fn is_normal(self) -> Mask<$mask_ty, LANES> {
|
||||
!(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
|
||||
}
|
||||
|
||||
/// Replaces each lane with a number that represents its sign.
|
||||
///
|
||||
/// * `1.0` if the number is positive, `+0.0`, or `INFINITY`
|
||||
/// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY`
|
||||
/// * `NAN` if the number is `NAN`
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn signum(self) -> Self {
|
||||
self.is_nan().select(Self::splat(<$type>::NAN), Self::splat(1.0).copysign(self))
|
||||
}
|
||||
|
||||
/// Returns each lane with the magnitude of `self` and the sign of `sign`.
|
||||
///
|
||||
/// If any lane is a `NAN`, then a `NAN` with the sign of `sign` is returned.
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
pub fn copysign(self, sign: Self) -> Self {
|
||||
let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
|
||||
let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
|
||||
Self::from_bits(sign_bit | magnitude)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_float_vector! { f32, u32, i32 }
|
||||
impl_float_vector! { f64, u64, i64 }
|
||||
use crate::simd::Simd;
|
||||
|
||||
/// A 64-bit SIMD vector with two elements of type `f32`.
|
||||
pub type f32x2 = Simd<f32, 2>;
|
||||
|
|
@ -161,73 +22,3 @@ pub type f64x4 = Simd<f64, 4>;
|
|||
|
||||
/// A 512-bit SIMD vector with eight elements of type `f64`.
|
||||
pub type f64x8 = Simd<f64, 8>;
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
use sealed::Sealed;
|
||||
|
||||
/// SIMD operations on vectors of floating point numbers.
|
||||
pub trait SimdFloat: Sized + Sealed {
|
||||
/// Returns the minimum of each lane.
|
||||
///
|
||||
/// If one of the values is `NAN`, then the other value is returned.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn simd_min(self, other: Self) -> Self;
|
||||
|
||||
/// Returns the maximum of each lane.
|
||||
///
|
||||
/// If one of the values is `NAN`, then the other value is returned.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn simd_max(self, other: Self) -> Self;
|
||||
|
||||
/// Restrict each lane to a certain interval unless it is NaN.
|
||||
///
|
||||
/// For each lane in `self`, returns the corresponding lane in `max` if the lane is
|
||||
/// greater than `max`, and the corresponding lane in `min` if the lane is less
|
||||
/// than `min`. Otherwise returns the lane in `self`.
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn simd_clamp(self, min: Self, max: Self) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! impl_simd_float {
|
||||
{ $($float:ty),* } => {
|
||||
$(
|
||||
impl <const LANES: usize> Sealed for Simd<$float, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
}
|
||||
|
||||
impl <const LANES: usize> SimdFloat for Simd<$float, LANES>
|
||||
where
|
||||
LaneCount<LANES>: SupportedLaneCount,
|
||||
{
|
||||
#[inline]
|
||||
#[must_use = "method returns a new vector and does not mutate the original value"]
|
||||
fn simd_min(self, other: Self) -> Self {
|
||||
unsafe { intrinsics::simd_fmin(self, other) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simd_max(self, other: Self) -> Self {
|
||||
unsafe { intrinsics::simd_fmax(self, other) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simd_clamp(self, min: Self, max: Self) -> Self {
|
||||
assert!(
|
||||
min.simd_le(max).all(),
|
||||
"each lane in `min` must be less than or equal to the corresponding lane in `max`",
|
||||
);
|
||||
let mut x = self;
|
||||
x = x.simd_lt(min).select(min, x);
|
||||
x = x.simd_gt(max).select(max, x);
|
||||
x
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl_simd_float! { f32, f64 }
|
||||
|
|
|
|||
|
|
@ -348,6 +348,7 @@ macro_rules! impl_unsigned_tests {
|
|||
macro_rules! impl_float_tests {
|
||||
{ $scalar:tt, $int_scalar:tt } => {
|
||||
mod $scalar {
|
||||
use core_simd::SimdFloat;
|
||||
type Vector<const LANES: usize> = core_simd::Simd<Scalar, LANES>;
|
||||
type Scalar = $scalar;
|
||||
|
||||
|
|
@ -464,7 +465,6 @@ macro_rules! impl_float_tests {
|
|||
}
|
||||
|
||||
fn simd_min<const LANES: usize>() {
|
||||
use core_simd::simd::SimdFloat;
|
||||
// Regular conditions (both values aren't zero)
|
||||
test_helpers::test_binary_elementwise(
|
||||
&Vector::<LANES>::simd_min,
|
||||
|
|
@ -488,7 +488,6 @@ macro_rules! impl_float_tests {
|
|||
}
|
||||
|
||||
fn simd_max<const LANES: usize>() {
|
||||
use core_simd::simd::SimdFloat;
|
||||
// Regular conditions (both values aren't zero)
|
||||
test_helpers::test_binary_elementwise(
|
||||
&Vector::<LANES>::simd_max,
|
||||
|
|
@ -512,7 +511,6 @@ macro_rules! impl_float_tests {
|
|||
}
|
||||
|
||||
fn simd_clamp<const LANES: usize>() {
|
||||
use core_simd::simd::SimdFloat;
|
||||
test_helpers::test_3(&|value: [Scalar; LANES], mut min: [Scalar; LANES], mut max: [Scalar; LANES]| {
|
||||
for (min, max) in min.iter_mut().zip(max.iter_mut()) {
|
||||
if max < min {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue