float.rs - source
Description: Source of the Rust file `library/core/src/../../portable-simd/crates/core_simd/src/simd/num/float.rs`.

#
core/portable-simd/crates/core_simd/src/simd/num/
float.rs

    1use super::sealed::Sealed;
    2use crate::simd::{
    3    LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount,
    4    cmp::{SimdPartialEq, SimdPartialOrd},
    5};
    6
    7/// Operations on SIMD vectors of floats.
    8pub trait SimdFloat: Copy + Sealed {
    9    /// Mask type used for manipulating this SIMD vector type.
    10    type Mask;
    11
    12    /// Scalar type contained by this SIMD vector type.
    13    type Scalar;
    14
    15    /// Bit representation of this SIMD vector type.
    16    type Bits;
    17
    18    /// A SIMD vector with a different element type.
    19    type Cast<T: SimdElement>;
    20
    21    /// Performs elementwise conversion of this vector's elements to another SIMD-valid type.
    22    ///
    23    /// This follows the semantics of Rust's `as` conversion for floats (truncating or saturating
    24    /// at the limits) for each element.
    25    ///
    26    /// # Example
    27    /// ```
    28    /// # #![feature(portable_simd)]
    29    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
    30    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
    31    /// # use simd::prelude::*;
    32    /// let floats: Simd<f32, 4> = Simd::from_array([1.9, -4.5, f32::INFINITY, f32::NAN]);
    33    /// let ints = floats.cast::<i32>();
    34    /// assert_eq!(ints, Simd::from_array([1, -4, i32::MAX, 0]));
    35    ///
    36    /// // Formally equivalent, but `Simd::cast` can optimize better.
    37    /// assert_eq!(ints, Simd::from_array(floats.to_array().map(|x| x as i32)));
    38    ///
    39    /// // The float conversion does not round-trip.
    40    /// let floats_again = ints.cast();
    41    /// assert_ne!(floats, floats_again);
    42    /// assert_eq!(floats_again, Simd::from_array([1.0, -4.0, 2147483647.0, 0.0]));
    43    /// ```
    44    #[must_use]
    45    fn cast<T: SimdCast>(self) -> Self::Cast<T>;
    46
    47    /// Rounds toward zero and converts to the same-width integer type, assuming that
    48    /// the value is finite and fits in that type.
    49    ///
    50    /// # Safety
    51    /// The value must:
    52    ///
    53    /// * Not be NaN
    54    /// * Not be infinite
    55    /// * Be representable in the return type, after truncating off its fractional part
    56    ///
    57    /// If these requirements are infeasible or costly, consider using the safe function [cast],
    58    /// which saturates on conversion.
    59    ///
    60    /// [cast]: Simd::cast
    61    unsafe fn to_int_unchecked<I: SimdCast>(self) -> Self::Cast<I>
    62    where
    63        Self::Scalar: core::convert::FloatToInt<I>;
    64
    65    /// Raw transmutation to an unsigned integer vector type with the
    66    /// same size and number of elements.
    67    #[must_use = "method returns a new vector and does not mutate the original value"]
    68    fn to_bits(self) -> Self::Bits;
    69
    70    /// Raw transmutation from an unsigned integer vector type with the
    71    /// same size and number of elements.
    72    #[must_use = "method returns a new vector and does not mutate the original value"]
    73    fn from_bits(bits: Self::Bits) -> Self;
    74
    75    /// Produces a vector where every element has the absolute value of the
    76    /// equivalently-indexed element in `self`.
    77    #[must_use = "method returns a new vector and does not mutate the original value"]
    78    fn abs(self) -> Self;
    79
    80    /// Takes the reciprocal (inverse) of each element, `1/x`.
    81    #[must_use = "method returns a new vector and does not mutate the original value"]
    82    fn recip(self) -> Self;
    83
    84    /// Converts each element from radians to degrees.
    85    #[must_use = "method returns a new vector and does not mutate the original value"]
    86    fn to_degrees(self) -> Self;
    87
    88    /// Converts each element from degrees to radians.
    89    #[must_use = "method returns a new vector and does not mutate the original value"]
    90    fn to_radians(self) -> Self;
    91
    92    /// Returns true for each element if it has a positive sign, including
    93    /// `+0.0`, `NaN`s with positive sign bit and positive infinity.
    94    #[must_use = "method returns a new mask and does not mutate the original value"]
    95    fn is_sign_positive(self) -> Self::Mask;
    96
    97    /// Returns true for each element if it has a negative sign, including
    98    /// `-0.0`, `NaN`s with negative sign bit and negative infinity.
    99    #[must_use = "method returns a new mask and does not mutate the original value"]
    100    fn is_sign_negative(self) -> Self::Mask;
    101
    102    /// Returns true for each element if its value is `NaN`.
    103    #[must_use = "method returns a new mask and does not mutate the original value"]
    104    fn is_nan(self) -> Self::Mask;
    105
    106    /// Returns true for each element if its value is positive infinity or negative infinity.
    107    #[must_use = "method returns a new mask and does not mutate the original value"]
    108    fn is_infinite(self) -> Self::Mask;
    109
    110    /// Returns true for each element if its value is neither infinite nor `NaN`.
    111    #[must_use = "method returns a new mask and does not mutate the original value"]
    112    fn is_finite(self) -> Self::Mask;
    113
    114    /// Returns true for each element if its value is subnormal.
    115    #[must_use = "method returns a new mask and does not mutate the original value"]
    116    fn is_subnormal(self) -> Self::Mask;
    117
    118    /// Returns true for each element if its value is neither zero, infinite,
    119    /// subnormal, nor `NaN`.
    120    #[must_use = "method returns a new mask and does not mutate the original value"]
    121    fn is_normal(self) -> Self::Mask;
    122
    123    /// Replaces each element with a number that represents its sign.
    124    ///
    125    /// * `1.0` if the number is positive, `+0.0`, or `INFINITY`
    126    /// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY`
    127    /// * `NAN` if the number is `NAN`
    128    #[must_use = "method returns a new vector and does not mutate the original value"]
    129    fn signum(self) -> Self;
    130
    131    /// Returns each element with the magnitude of `self` and the sign of `sign`.
    132    ///
    133    /// For any element containing a `NAN`, a `NAN` with the sign of `sign` is returned.
    134    #[must_use = "method returns a new vector and does not mutate the original value"]
    135    fn copysign(self, sign: Self) -> Self;
    136
    137    /// Returns the minimum of each element.
    138    ///
    139    /// If one of the values is `NAN`, then the other value is returned.
    140    #[must_use = "method returns a new vector and does not mutate the original value"]
    141    fn simd_min(self, other: Self) -> Self;
    142
    143    /// Returns the maximum of each element.
    144    ///
    145    /// If one of the values is `NAN`, then the other value is returned.
    146    #[must_use = "method returns a new vector and does not mutate the original value"]
    147    fn simd_max(self, other: Self) -> Self;
    148
    149    /// Restrict each element to a certain interval unless it is NaN.
    150    ///
    151    /// For each element in `self`, returns the corresponding element in `max` if the element is
    152    /// greater than `max`, and the corresponding element in `min` if the element is less
    153    /// than `min`.  Otherwise returns the element in `self`.
    154    #[must_use = "method returns a new vector and does not mutate the original value"]
    155    fn simd_clamp(self, min: Self, max: Self) -> Self;
    156
    157    /// Returns the sum of the elements of the vector.
    158    ///
    159    /// # Examples
    160    ///
    161    /// ```
    162    /// # #![feature(portable_simd)]
    163    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
    164    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
    165    /// # use simd::prelude::*;
    166    /// let v = f32x2::from_array([1., 2.]);
    167    /// assert_eq!(v.reduce_sum(), 3.);
    168    /// ```
    169    fn reduce_sum(self) -> Self::Scalar;
    170
    171    /// Reducing multiply.  Returns the product of the elements of the vector.
    172    ///
    173    /// # Examples
    174    ///
    175    /// ```
    176    /// # #![feature(portable_simd)]
    177    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
    178    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
    179    /// # use simd::prelude::*;
    180    /// let v = f32x2::from_array([3., 4.]);
    181    /// assert_eq!(v.reduce_product(), 12.);
    182    /// ```
    183    fn reduce_product(self) -> Self::Scalar;
    184
    185    /// Returns the maximum element in the vector.
    186    ///
    187    /// Returns values based on equality, so a vector containing both `0.` and `-0.` may
    188    /// return either.
    189    ///
    190    /// This function will not return `NaN` unless all elements are `NaN`.
    191    ///
    192    /// # Examples
    193    ///
    194    /// ```
    195    /// # #![feature(portable_simd)]
    196    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
    197    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
    198    /// # use simd::prelude::*;
    199    /// let v = f32x2::from_array([1., 2.]);
    200    /// assert_eq!(v.reduce_max(), 2.);
    201    ///
    202    /// // NaN values are skipped...
    203    /// let v = f32x2::from_array([1., f32::NAN]);
    204    /// assert_eq!(v.reduce_max(), 1.);
    205    ///
    206    /// // ...unless all values are NaN
    207    /// let v = f32x2::from_array([f32::NAN, f32::NAN]);
    208    /// assert!(v.reduce_max().is_nan());
    209    /// ```
    210    fn reduce_max(self) -> Self::Scalar;
    211
    212    /// Returns the minimum element in the vector.
    213    ///
    214    /// Returns values based on equality, so a vector containing both `0.` and `-0.` may
    215    /// return either.
    216    ///
    217    /// This function will not return `NaN` unless all elements are `NaN`.
    218    ///
    219    /// # Examples
    220    ///
    221    /// ```
    222    /// # #![feature(portable_simd)]
    223    /// # #[cfg(feature = "as_crate")] use core_simd::simd;
    224    /// # #[cfg(not(feature = "as_crate"))] use core::simd;
    225    /// # use simd::prelude::*;
    226    /// let v = f32x2::from_array([3., 7.]);
    227    /// assert_eq!(v.reduce_min(), 3.);
    228    ///
    229    /// // NaN values are skipped...
    230    /// let v = f32x2::from_array([1., f32::NAN]);
    231    /// assert_eq!(v.reduce_min(), 1.);
    232    ///
    233    /// // ...unless all values are NaN
    234    /// let v = f32x2::from_array([f32::NAN, f32::NAN]);
    235    /// assert!(v.reduce_min().is_nan());
    236    /// ```
    237    fn reduce_min(self) -> Self::Scalar;
    238}
    239
    240macro_rules! impl_trait {
    241    { $($ty:ty { bits: $bits_ty:ty, mask: $mask_ty:ty }),* } => {
    242        $(
    243        impl<const N: usize> Sealed for Simd<$ty, N>
    244        where
    245            LaneCount<N>: SupportedLaneCount,
    246        {
    247        }
    248
    249        impl<const N: usize> SimdFloat for Simd<$ty, N>
    250        where
    251            LaneCount<N>: SupportedLaneCount,
    252        {
    253            type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>;
    254            type Scalar = $ty;
    255            type Bits = Simd<$bits_ty, N>;
    256            type Cast<T: SimdElement> = Simd<T, N>;
    257
    258            #[cfg(not(target_arch = "aarch64"))]
    259            #[inline]
    260            fn cast<T: SimdCast>(self) -> Self::Cast<T>
    261            {
    262                // Safety: supported types are guaranteed by SimdCast
    263                unsafe { core::intrinsics::simd::simd_as(self) }
    264            }
    265
    266            // workaround for https://github.com/llvm/llvm-project/issues/94694 (fixed in LLVM 20)
    267            // tracked in: https://github.com/rust-lang/rust/issues/135982
    268            #[cfg(target_arch = "aarch64")]
    269            #[inline]
    270            fn cast<T: SimdCast>(self) -> Self::Cast<T>
    271            {
    272                const { assert!(N <= 64) };
    273                if N <= 2 || N == 4 || N == 8 || N == 16 || N == 32 || N == 64 {
    274                    // Safety: supported types are guaranteed by SimdCast
    275                    unsafe { core::intrinsics::simd::simd_as(self) }
    276                } else if N < 4 {
    277                    let x = self.resize::<4>(Default::default()).cast();
    278                    x.resize::<N>(x[0])
    279                } else if N < 8 {
    280                    let x = self.resize::<8>(Default::default()).cast();
    281                    x.resize::<N>(x[0])
    282                } else if N < 16 {
    283                    let x = self.resize::<16>(Default::default()).cast();
    284                    x.resize::<N>(x[0])
    285                } else if N < 32 {
    286                    let x = self.resize::<32>(Default::default()).cast();
    287                    x.resize::<N>(x[0])
    288                } else {
    289                    let x = self.resize::<64>(Default::default()).cast();
    290                    x.resize::<N>(x[0])
    291                }
    292            }
    293
    294            #[inline]
    295            #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
    296            unsafe fn to_int_unchecked<I: SimdCast>(self) -> Self::Cast<I>
    297            where
    298                Self::Scalar: core::convert::FloatToInt<I>,
    299            {
    300                // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants
    301                unsafe { core::intrinsics::simd::simd_cast(self) }
    302            }
    303
    304            #[inline]
    305            fn to_bits(self) -> Simd<$bits_ty, N> {
    306                assert_eq!(size_of::<Self>(), size_of::<Self::Bits>());
    307                // Safety: transmuting between vector types is safe
    308                unsafe { core::mem::transmute_copy(&self) }
    309            }
    310
    311            #[inline]
    312            fn from_bits(bits: Simd<$bits_ty, N>) -> Self {
    313                assert_eq!(size_of::<Self>(), size_of::<Self::Bits>());
    314                // Safety: transmuting between vector types is safe
    315                unsafe { core::mem::transmute_copy(&bits) }
    316            }
    317
    318            #[inline]
    319            fn abs(self) -> Self {
    320                // Safety: `self` is a float vector
    321                unsafe { core::intrinsics::simd::simd_fabs(self) }
    322            }
    323
    324            #[inline]
    325            fn recip(self) -> Self {
    326                Self::splat(1.0) / self
    327            }
    328
    329            #[inline]
    330            fn to_degrees(self) -> Self {
    331                // to_degrees uses a special constant for better precision, so extract that constant
    332                self * Self::splat(Self::Scalar::to_degrees(1.))
    333            }
    334
    335            #[inline]
    336            fn to_radians(self) -> Self {
    337                self * Self::splat(Self::Scalar::to_radians(1.))
    338            }
    339
    340            #[inline]
    341            fn is_sign_positive(self) -> Self::Mask {
    342                !self.is_sign_negative()
    343            }
    344
    345            #[inline]
    346            fn is_sign_negative(self) -> Self::Mask {
    347                let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
    348                sign_bits.simd_gt(Simd::splat(0))
    349            }
    350
    351            #[inline]
    352            fn is_nan(self) -> Self::Mask {
    353                self.simd_ne(self)
    354            }
    355
    356            #[inline]
    357            fn is_infinite(self) -> Self::Mask {
    358                self.abs().simd_eq(Self::splat(Self::Scalar::INFINITY))
    359            }
    360
    361            #[inline]
    362            fn is_finite(self) -> Self::Mask {
    363                self.abs().simd_lt(Self::splat(Self::Scalar::INFINITY))
    364            }
    365
    366            #[inline]
    367            fn is_subnormal(self) -> Self::Mask {
    368                // On some architectures (e.g. armv7 and some ppc) subnormals are flushed to zero,
    369                // so this comparison must be done with integers.
    370                let not_zero = self.abs().to_bits().simd_ne(Self::splat(0.0).to_bits());
    371                not_zero & (self.to_bits() & Self::splat(Self::Scalar::INFINITY).to_bits()).simd_eq(Simd::splat(0))
    372            }
    373
    374            #[inline]
    375            fn is_normal(self) -> Self::Mask {
    376                !(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
    377            }
    378
    379            #[inline]
    380            fn signum(self) -> Self {
    381                self.is_nan().select(Self::splat(Self::Scalar::NAN), Self::splat(1.0).copysign(self))
    382            }
    383
    384            #[inline]
    385            fn copysign(self, sign: Self) -> Self {
    386                let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
    387                let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
    388                Self::from_bits(sign_bit | magnitude)
    389            }
    390
    391            #[inline]
    392            fn simd_min(self, other: Self) -> Self {
    393                // Safety: `self` and `other` are float vectors
    394                unsafe { core::intrinsics::simd::simd_fmin(self, other) }
    395            }
    396
    397            #[inline]
    398            fn simd_max(self, other: Self) -> Self {
    399                // Safety: `self` and `other` are floating point vectors
    400                unsafe { core::intrinsics::simd::simd_fmax(self, other) }
    401            }
    402
    403            #[inline]
    404            fn simd_clamp(self, min: Self, max: Self) -> Self {
    405                assert!(
    406                    min.simd_le(max).all(),
    407                    "each element in `min` must be less than or equal to the corresponding element in `max`",
    408                );
    409                let mut x = self;
    410                x = x.simd_lt(min).select(min, x);
    411                x = x.simd_gt(max).select(max, x);
    412                x
    413            }
    414
    415            #[inline]
    416            fn reduce_sum(self) -> Self::Scalar {
    417                // LLVM sum is inaccurate on i586
    418                if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
    419                    self.as_array().iter().sum()
    420                } else {
    421                    // Safety: `self` is a float vector
    422                    unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, -0.) }
    423                }
    424            }
    425
    426            #[inline]
    427            fn reduce_product(self) -> Self::Scalar {
    428                // LLVM product is inaccurate on i586
    429                if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
    430                    self.as_array().iter().product()
    431                } else {
    432                    // Safety: `self` is a float vector
    433                    unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1.) }
    434                }
    435            }
    436
    437            #[inline]
    438            fn reduce_max(self) -> Self::Scalar {
    439                // Safety: `self` is a float vector
    440                unsafe { core::intrinsics::simd::simd_reduce_max(self) }
    441            }
    442
    443            #[inline]
    444            fn reduce_min(self) -> Self::Scalar {
    445                // Safety: `self` is a float vector
    446                unsafe { core::intrinsics::simd::simd_reduce_min(self) }
    447            }
    448        }
    449        )*
    450    }
    451}
    452
    453impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } }