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 } }