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