Skip to main content

core_simd/simd/num/
float.rs

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