| 1 | use super::sealed::Sealed; |
| 2 | use crate::simd::{ |
| 3 | LaneCount, Mask, Simd, SimdCast, SimdElement, SupportedLaneCount, |
| 4 | cmp::{SimdPartialEq, SimdPartialOrd}, |
| 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 | #[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 | |
| 453 | impl_trait! { f32 { bits: u32, mask: i32 }, f64 { bits: u64, mask: i64 } } |
| 454 | |