core_simd/simd/num/float.rs
1use super::sealed::Sealed;
2use crate::simd::{
3 Mask, Select, Simd, SimdCast, SimdElement,
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
245 impl<const N: usize> SimdFloat for Simd<$ty, N>
246 {
247 type Mask = Mask<<$mask_ty as SimdElement>::Mask, N>;
248 type Scalar = $ty;
249 type Bits = Simd<$bits_ty, N>;
250 type Cast<T: SimdElement> = Simd<T, N>;
251
252 #[cfg(not(target_arch = "aarch64"))]
253 #[inline]
254 fn cast<T: SimdCast>(self) -> Self::Cast<T>
255 {
256 // Safety: supported types are guaranteed by SimdCast
257 unsafe { core::intrinsics::simd::simd_as(self) }
258 }
259
260 // workaround for https://github.com/llvm/llvm-project/issues/94694 (fixed in LLVM 20)
261 // tracked in: https://github.com/rust-lang/rust/issues/135982
262 #[cfg(target_arch = "aarch64")]
263 #[inline]
264 fn cast<T: SimdCast>(self) -> Self::Cast<T>
265 {
266 const { assert!(N <= 64) };
267 if N <= 2 || N == 4 || N == 8 || N == 16 || N == 32 || N == 64 {
268 // Safety: supported types are guaranteed by SimdCast
269 unsafe { core::intrinsics::simd::simd_as(self) }
270 } else if N < 4 {
271 let x = self.resize::<4>(Default::default()).cast();
272 x.resize::<N>(x[0])
273 } else if N < 8 {
274 let x = self.resize::<8>(Default::default()).cast();
275 x.resize::<N>(x[0])
276 } else if N < 16 {
277 let x = self.resize::<16>(Default::default()).cast();
278 x.resize::<N>(x[0])
279 } else if N < 32 {
280 let x = self.resize::<32>(Default::default()).cast();
281 x.resize::<N>(x[0])
282 } else {
283 let x = self.resize::<64>(Default::default()).cast();
284 x.resize::<N>(x[0])
285 }
286 }
287
288 #[inline]
289 #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
290 unsafe fn to_int_unchecked<I: SimdCast>(self) -> Self::Cast<I>
291 where
292 Self::Scalar: core::convert::FloatToInt<I>,
293 {
294 // Safety: supported types are guaranteed by SimdCast, the caller is responsible for the extra invariants
295 unsafe { core::intrinsics::simd::simd_cast(self) }
296 }
297
298 #[inline]
299 fn to_bits(self) -> Simd<$bits_ty, N> {
300 assert_eq!(size_of::<Self>(), size_of::<Self::Bits>());
301 // Safety: transmuting between vector types is safe
302 unsafe { core::mem::transmute_copy(&self) }
303 }
304
305 #[inline]
306 fn from_bits(bits: Simd<$bits_ty, N>) -> Self {
307 assert_eq!(size_of::<Self>(), size_of::<Self::Bits>());
308 // Safety: transmuting between vector types is safe
309 unsafe { core::mem::transmute_copy(&bits) }
310 }
311
312 #[inline]
313 fn abs(self) -> Self {
314 // Safety: `self` is a float vector
315 unsafe { core::intrinsics::simd::simd_fabs(self) }
316 }
317
318 #[inline]
319 fn recip(self) -> Self {
320 Self::splat(1.0) / self
321 }
322
323 #[inline]
324 fn to_degrees(self) -> Self {
325 // to_degrees uses a special constant for better precision, so extract that constant
326 self * Self::splat(Self::Scalar::to_degrees(1.))
327 }
328
329 #[inline]
330 fn to_radians(self) -> Self {
331 self * Self::splat(Self::Scalar::to_radians(1.))
332 }
333
334 #[inline]
335 fn is_sign_positive(self) -> Self::Mask {
336 !self.is_sign_negative()
337 }
338
339 #[inline]
340 fn is_sign_negative(self) -> Self::Mask {
341 let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
342 sign_bits.simd_gt(Simd::splat(0))
343 }
344
345 #[inline]
346 fn is_nan(self) -> Self::Mask {
347 self.simd_ne(self)
348 }
349
350 #[inline]
351 fn is_infinite(self) -> Self::Mask {
352 self.abs().simd_eq(Self::splat(Self::Scalar::INFINITY))
353 }
354
355 #[inline]
356 fn is_finite(self) -> Self::Mask {
357 self.abs().simd_lt(Self::splat(Self::Scalar::INFINITY))
358 }
359
360 #[inline]
361 fn is_subnormal(self) -> Self::Mask {
362 // On some architectures (e.g. armv7 and some ppc) subnormals are flushed to zero,
363 // so this comparison must be done with integers.
364 let not_zero = self.abs().to_bits().simd_ne(Self::splat(0.0).to_bits());
365 not_zero & (self.to_bits() & Self::splat(Self::Scalar::INFINITY).to_bits()).simd_eq(Simd::splat(0))
366 }
367
368 #[inline]
369 fn is_normal(self) -> Self::Mask {
370 !(self.abs().simd_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
371 }
372
373 #[inline]
374 fn signum(self) -> Self {
375 self.is_nan().select(Self::splat(Self::Scalar::NAN), Self::splat(1.0).copysign(self))
376 }
377
378 #[inline]
379 fn copysign(self, sign: Self) -> Self {
380 let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
381 let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
382 Self::from_bits(sign_bit | magnitude)
383 }
384
385 #[inline]
386 fn simd_min(self, other: Self) -> Self {
387 // Safety: `self` and `other` are float vectors
388 unsafe { core::intrinsics::simd::simd_fmin(self, other) }
389 }
390
391 #[inline]
392 fn simd_max(self, other: Self) -> Self {
393 // Safety: `self` and `other` are floating point vectors
394 unsafe { core::intrinsics::simd::simd_fmax(self, other) }
395 }
396
397 #[inline]
398 fn simd_clamp(self, min: Self, max: Self) -> Self {
399 assert!(
400 min.simd_le(max).all(),
401 "each element in `min` must be less than or equal to the corresponding element in `max`",
402 );
403 let mut x = self;
404 x = x.simd_lt(min).select(min, x);
405 x = x.simd_gt(max).select(max, x);
406 x
407 }
408
409 #[inline]
410 fn reduce_sum(self) -> Self::Scalar {
411 // LLVM sum is inaccurate on i586
412 if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
413 self.as_array().iter().sum()
414 } else {
415 // Safety: `self` is a float vector
416 unsafe { core::intrinsics::simd::simd_reduce_add_ordered(self, -0.) }
417 }
418 }
419
420 #[inline]
421 fn reduce_product(self) -> Self::Scalar {
422 // LLVM product is inaccurate on i586
423 if cfg!(all(target_arch = "x86", not(target_feature = "sse2"))) {
424 self.as_array().iter().product()
425 } else {
426 // Safety: `self` is a float vector
427 unsafe { core::intrinsics::simd::simd_reduce_mul_ordered(self, 1.) }
428 }
429 }
430
431 #[inline]
432 fn reduce_max(self) -> Self::Scalar {
433 // Safety: `self` is a float vector
434 unsafe { core::intrinsics::simd::simd_reduce_max(self) }
435 }
436
437 #[inline]
438 fn reduce_min(self) -> Self::Scalar {
439 // Safety: `self` is a float vector
440 unsafe { core::intrinsics::simd::simd_reduce_min(self) }
441 }
442 }
443 )*
444 }
445}
446
447impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } }