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