1 | use super::sealed::Sealed; |
2 | use crate::simd::{ |
3 | cmp::{SimdPartialEq, SimdPartialOrd}, |
4 | LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, |
5 | }; |
6 | |
7 | /// Operations on SIMD vectors of floats. |
8 | pub 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 | |
240 | macro_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 | |
425 | impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } |
426 | |