Skip to main content

wide/
f32x4_.rs

1use super::*;
2
3pick! {
4  if #[cfg(target_feature="sse")] {
5    /// A SIMD vector with four elements of type [`f32`].
6    ///
7    /// See the [crate level documentation] for more information about SIMD
8    /// vectors.
9    ///
10    /// [crate level documentation]: crate
11    #[derive(Default, Clone, Copy, PartialEq)]
12    #[repr(C, align(16))]
13    pub struct f32x4 { pub(crate) sse: m128 }
14  } else if #[cfg(target_feature="simd128")] {
15    use core::arch::wasm32::*;
16
17    /// A SIMD vector with four elements of type [`f32`].
18    ///
19    /// See the [crate level documentation] for more information about SIMD
20    /// vectors.
21    ///
22    /// [crate level documentation]: crate
23    #[derive(Clone, Copy)]
24    #[repr(transparent)]
25    pub struct f32x4 { pub(crate) simd: v128 }
26
27    impl Default for f32x4 {
28      fn default() -> Self {
29        Self::splat(0.0)
30      }
31    }
32
33    impl PartialEq for f32x4 {
34      fn eq(&self, other: &Self) -> bool {
35        u32x4_all_true(f32x4_eq(self.simd, other.simd))
36      }
37    }
38  } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
39    use core::arch::aarch64::*;
40
41    /// A SIMD vector with four elements of type [`f32`].
42    ///
43    /// See the [crate level documentation] for more information about SIMD
44    /// vectors.
45    ///
46    /// [crate level documentation]: crate
47    #[repr(C)]
48    #[derive(Copy, Clone)]
49    pub struct f32x4 { pub(crate) neon : float32x4_t }
50
51    impl Default for f32x4 {
52      #[inline]
53      fn default() -> Self {
54        unsafe { Self { neon: vdupq_n_f32(0.0)} }
55      }
56    }
57
58    impl PartialEq for f32x4 {
59      #[inline]
60      fn eq(&self, other: &Self) -> bool {
61        unsafe { vminvq_u32(vceqq_f32(self.neon, other.neon))==u32::MAX }
62      }
63
64    }
65    } else {
66    /// A SIMD vector with four elements of type [`f32`].
67    ///
68    /// See the [crate level documentation] for more information about SIMD
69    /// vectors.
70    ///
71    /// [crate level documentation]: crate
72    #[derive(Default, Clone, Copy, PartialEq)]
73    #[repr(C, align(16))]
74    pub struct f32x4 { pub(crate) arr: [f32;4] }
75  }
76}
77
78macro_rules! const_f32_as_f32x4 {
79  ($i:ident, $f:expr) => {
80    #[allow(non_upper_case_globals)]
81    pub const $i: f32x4 = f32x4::new([$f; 4]);
82  };
83}
84
85impl_simd! {
86  unsafe {
87    T = f32,
88    N = 4,
89    Simd = f32x4,
90    optional_type_x86_inner { X86Inner = __m128 },
91    optional_type_arm_inner { ArmInner = float32x4_t },
92    optional_type_wasm_inner { WasmInner = v128 },
93  }
94
95  #[inline]
96  fn simd_eq(self, rhs: Self) -> Self::Output {
97    pick! {
98      if #[cfg(target_feature="sse")] {
99        Self { sse: cmp_eq_mask_m128(self.sse, rhs.sse) }
100      } else if #[cfg(target_feature="simd128")] {
101        Self { simd: f32x4_eq(self.simd, rhs.simd) }
102      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
103        unsafe {Self { neon: vreinterpretq_f32_u32(vceqq_f32(self.neon, rhs.neon)) }}
104      } else {
105        Self { arr: [
106          if self.arr[0] == rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
107          if self.arr[1] == rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
108          if self.arr[2] == rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
109          if self.arr[3] == rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
110        ]}
111      }
112    }
113  }
114
115  #[inline]
116  fn simd_ne(self, rhs: Self) -> Self::Output {
117    pick! {
118      if #[cfg(target_feature="sse")] {
119        Self { sse: cmp_neq_mask_m128(self.sse, rhs.sse) }
120      } else if #[cfg(target_feature="simd128")] {
121        Self { simd: f32x4_ne(self.simd, rhs.simd) }
122      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
123        unsafe {Self { neon: vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(self.neon, rhs.neon))) }}
124      } else {
125        Self { arr: [
126          if self.arr[0] != rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
127          if self.arr[1] != rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
128          if self.arr[2] != rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
129          if self.arr[3] != rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
130        ]}
131      }
132    }
133  }
134
135  #[inline]
136  fn simd_lt(self, rhs: Self) -> Self::Output {
137    pick! {
138      if #[cfg(target_feature="sse")] {
139        Self { sse: cmp_lt_mask_m128(self.sse, rhs.sse) }
140      } else if #[cfg(target_feature="simd128")] {
141        Self { simd: f32x4_lt(self.simd, rhs.simd) }
142      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
143        unsafe {Self { neon: vreinterpretq_f32_u32(vcltq_f32(self.neon, rhs.neon)) }}
144      } else {
145        Self { arr: [
146          if self.arr[0] < rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
147          if self.arr[1] < rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
148          if self.arr[2] < rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
149          if self.arr[3] < rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
150        ]}
151      }
152    }
153  }
154
155  #[inline]
156  fn simd_gt(self, rhs: Self) -> Self::Output {
157    pick! {
158      if #[cfg(target_feature="sse")] {
159        Self { sse: cmp_gt_mask_m128(self.sse, rhs.sse) }
160      } else if #[cfg(target_feature="simd128")] {
161        Self { simd: f32x4_gt(self.simd, rhs.simd) }
162      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
163        unsafe {Self { neon: vreinterpretq_f32_u32(vcgtq_f32(self.neon, rhs.neon)) }}
164      } else {
165        Self { arr: [
166          if self.arr[0] > rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
167          if self.arr[1] > rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
168          if self.arr[2] > rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
169          if self.arr[3] > rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
170        ]}
171      }
172    }
173  }
174
175  #[inline]
176  fn simd_le(self, rhs: Self) -> Self::Output {
177    pick! {
178      if #[cfg(target_feature="sse")] {
179        Self { sse: cmp_le_mask_m128(self.sse, rhs.sse) }
180      } else if #[cfg(target_feature="simd128")] {
181        Self { simd: f32x4_le(self.simd, rhs.simd) }
182      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
183        unsafe {Self { neon: vreinterpretq_f32_u32(vcleq_f32(self.neon, rhs.neon)) }}
184      } else {
185        Self { arr: [
186          if self.arr[0] <= rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
187          if self.arr[1] <= rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
188          if self.arr[2] <= rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
189          if self.arr[3] <= rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
190        ]}
191      }
192    }
193  }
194
195  #[inline]
196  fn simd_ge(self, rhs: Self) -> Self::Output {
197    pick! {
198      if #[cfg(target_feature="sse")] {
199        Self { sse: cmp_ge_mask_m128(self.sse, rhs.sse) }
200      } else if #[cfg(target_feature="simd128")] {
201        Self { simd: f32x4_ge(self.simd, rhs.simd) }
202      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
203        unsafe {Self { neon: vreinterpretq_f32_u32(vcgeq_f32(self.neon, rhs.neon)) }}
204      } else {
205        Self { arr: [
206          if self.arr[0] >= rhs.arr[0] { f32::from_bits(u32::MAX) } else { 0.0 },
207          if self.arr[1] >= rhs.arr[1] { f32::from_bits(u32::MAX) } else { 0.0 },
208          if self.arr[2] >= rhs.arr[2] { f32::from_bits(u32::MAX) } else { 0.0 },
209          if self.arr[3] >= rhs.arr[3] { f32::from_bits(u32::MAX) } else { 0.0 },
210        ]}
211      }
212    }
213  }
214
215  #[inline]
216  pub fn bitselect(self, if_one: Self, if_zero: Self) -> Self {
217    pick! {
218      if #[cfg(target_feature="sse2")] {
219        Self {
220          sse: bitor_m128(
221            bitand_m128(if_one.sse, self.sse),
222            bitandnot_m128(self.sse, if_zero.sse),
223          ),
224        }
225      } else if #[cfg(target_feature="simd128")] {
226        Self { simd: v128_bitselect(if_one.simd, if_zero.simd, self.simd) }
227      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
228        unsafe {Self { neon: vbslq_f32(vreinterpretq_u32_f32(self.neon), if_one.neon, if_zero.neon) }}
229      } else {
230        generic_bit_blend(self, if_one, if_zero)
231      }
232    }
233  }
234
235  #[inline]
236  pub fn select(self, if_true: Self, if_false: Self) -> Self {
237    pick! {
238      if #[cfg(target_feature="sse4.1")] {
239        Self { sse: blend_varying_m128(if_false.sse, if_true.sse, self.sse) }
240      } else if #[cfg(target_feature="simd128")] {
241        Self { simd: v128_bitselect(if_true.simd, if_false.simd, self.simd) }
242      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
243        unsafe {Self { neon: vbslq_f32(vreinterpretq_u32_f32(self.neon), if_true.neon, if_false.neon) }}
244      } else {
245        generic_bit_blend(self, if_true, if_false)
246      }
247    }
248  }
249
250  #[inline]
251  pub fn to_bitmask(self) -> u32 {
252    pick! {
253      if #[cfg(target_feature="sse")] {
254        move_mask_m128(self.sse) as u32
255      } else if #[cfg(target_feature="simd128")] {
256        u32x4_bitmask(self.simd) as u32
257      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
258        unsafe
259        {
260          // set all to 1 if top bit is set, else 0
261          let masked = vcltq_s32( vreinterpretq_s32_f32(self.neon), vdupq_n_s32(0));
262
263          // select the right bit out of each lane
264          let selectbit : uint32x4_t = core::mem::transmute([1u32, 2, 4, 8]);
265          let r = vandq_u32(masked, selectbit);
266
267          // horizontally add the 16-bit lanes
268          vaddvq_u32(r) as u32
269        }
270      } else {
271        (((self.arr[0].to_bits() as i32) < 0) as u32) |
272        (((self.arr[1].to_bits() as i32) < 0) as u32) << 1 |
273        (((self.arr[2].to_bits() as i32) < 0) as u32) << 2 |
274        (((self.arr[3].to_bits() as i32) < 0) as u32) << 3
275      }
276    }
277  }
278
279  #[inline]
280  pub fn any(self) -> bool {
281    pick! {
282      if #[cfg(target_feature="simd128")] {
283        v128_any_true(self.simd)
284      } else {
285        self.to_bitmask() != 0
286      }
287    }
288  }
289
290  #[inline]
291  pub fn all(self) -> bool {
292    pick! {
293      if #[cfg(target_feature="simd128")] {
294        u32x4_all_true(self.simd)
295      } else {
296        // four lanes
297        self.to_bitmask() == 0b1111
298      }
299    }
300  }
301
302  ///
303  /// Currently this function is only accelerated on `sse`.
304  #[inline]
305  pub fn transpose(data: [f32x4; 4]) -> [f32x4; 4] {
306    pick! {
307      if #[cfg(target_feature="sse")] {
308        let mut e0 = data[0];
309        let mut e1 = data[1];
310        let mut e2 = data[2];
311        let mut e3 = data[3];
312
313        transpose_four_m128(&mut e0.sse, &mut e1.sse, &mut e2.sse, &mut e3.sse);
314
315        [e0, e1, e2, e3]
316      } else if #[cfg(any(all(target_feature="neon",target_arch="aarch64"), target_feature="simd128"))] {
317        let a = data[0].unpack_lo(data[2]);
318        let b = data[1].unpack_lo(data[3]);
319        let c = data[0].unpack_hi(data[2]);
320        let d = data[1].unpack_hi(data[3]);
321
322        [
323          a.unpack_lo(b),
324          a.unpack_hi(b),
325          c.unpack_lo(d),
326          c.unpack_hi(d),
327        ]
328      } else {
329        #[inline(always)]
330        fn transpose_column(data: &[f32x4; 4], index: usize) -> f32x4 {
331          f32x4::new([
332            data[0].as_array()[index],
333            data[1].as_array()[index],
334            data[2].as_array()[index],
335            data[3].as_array()[index],
336          ])
337        }
338
339        [
340          transpose_column(&data, 0),
341          transpose_column(&data, 1),
342          transpose_column(&data, 2),
343          transpose_column(&data, 3),
344        ]
345      }
346    }
347  }
348}
349
350impl_simd_float! {
351  unsafe {
352    T = f32,
353    N = 4,
354    Simd = f32x4,
355    UnsignedT = u32,
356    UnsignedSimd = u32x4,
357  }
358  old_powf_simd_fn_name = pow_f32x4,
359
360  #[inline]
361  fn neg(self) -> Self::Output {
362    pick! {
363      if #[cfg(target_feature="sse")] {
364        Self { sse: bitxor_m128(self.sse, Self::splat(-0.0).sse) }
365      } else if #[cfg(target_feature="simd128")] {
366        Self { simd: f32x4_neg(self.simd) }
367      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
368        unsafe {Self { neon: vnegq_f32(self.neon) }}
369      } else {
370        Self { arr: [
371          -self.arr[0],
372          -self.arr[1],
373          -self.arr[2],
374          -self.arr[3],
375        ]}
376      }
377    }
378  }
379
380  #[inline]
381  fn not(self) -> Self::Output {
382    self ^ cast::<u128, f32x4>(u128::MAX)
383  }
384
385  #[inline]
386  fn add(self, rhs: Self) -> Self::Output {
387    pick! {
388      if #[cfg(target_feature="sse")] {
389        Self { sse: add_m128(self.sse, rhs.sse) }
390      } else if #[cfg(target_feature="simd128")] {
391        Self { simd: f32x4_add(self.simd, rhs.simd) }
392      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
393        unsafe { Self { neon: vaddq_f32(self.neon, rhs.neon) } }
394      } else {
395        Self { arr: [
396          self.arr[0] + rhs.arr[0],
397          self.arr[1] + rhs.arr[1],
398          self.arr[2] + rhs.arr[2],
399          self.arr[3] + rhs.arr[3],
400        ]}
401      }
402    }
403  }
404
405  #[inline]
406  fn sub(self, rhs: Self) -> Self::Output {
407    pick! {
408      if #[cfg(target_feature="sse")] {
409        Self { sse: sub_m128(self.sse, rhs.sse) }
410      } else if #[cfg(target_feature="simd128")] {
411        Self { simd: f32x4_sub(self.simd, rhs.simd) }
412      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
413        unsafe {Self { neon: vsubq_f32(self.neon, rhs.neon) }}
414      } else {
415        Self { arr: [
416          self.arr[0] - rhs.arr[0],
417          self.arr[1] - rhs.arr[1],
418          self.arr[2] - rhs.arr[2],
419          self.arr[3] - rhs.arr[3],
420        ]}
421      }
422    }
423  }
424
425  #[inline]
426  fn mul(self, rhs: Self) -> Self::Output {
427    pick! {
428      if #[cfg(target_feature="sse")] {
429        Self { sse: mul_m128(self.sse, rhs.sse) }
430      } else if #[cfg(target_feature="simd128")] {
431        Self { simd: f32x4_mul(self.simd, rhs.simd) }
432      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
433        unsafe {Self { neon: vmulq_f32(self.neon, rhs.neon) }}
434      } else {
435        Self { arr: [
436          self.arr[0] * rhs.arr[0],
437          self.arr[1] * rhs.arr[1],
438          self.arr[2] * rhs.arr[2],
439          self.arr[3] * rhs.arr[3],
440        ]}
441      }
442    }
443  }
444
445  #[inline]
446  fn div(self, rhs: Self) -> Self::Output {
447    pick! {
448      if #[cfg(target_feature="sse")] {
449        Self { sse: div_m128(self.sse, rhs.sse) }
450      } else if #[cfg(target_feature="simd128")] {
451        Self { simd: f32x4_div(self.simd, rhs.simd) }
452      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
453        unsafe {Self { neon: vdivq_f32(self.neon, rhs.neon) }}
454      } else {
455        Self { arr: [
456          self.arr[0] / rhs.arr[0],
457          self.arr[1] / rhs.arr[1],
458          self.arr[2] / rhs.arr[2],
459          self.arr[3] / rhs.arr[3],
460        ]}
461      }
462    }
463  }
464
465  #[inline]
466  fn rem(self, rhs: Self) -> Self::Output {
467    Self::new([
468      self.to_array()[0] % rhs.to_array()[0],
469      self.to_array()[1] % rhs.to_array()[1],
470      self.to_array()[2] % rhs.to_array()[2],
471      self.to_array()[3] % rhs.to_array()[3],
472    ])
473  }
474
475  #[inline]
476  fn bitand(self, rhs: Self) -> Self::Output {
477    pick! {
478      if #[cfg(target_feature="sse")] {
479        Self { sse: bitand_m128(self.sse, rhs.sse) }
480      } else if #[cfg(target_feature="simd128")] {
481        Self { simd: v128_and(self.simd, rhs.simd) }
482      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
483        unsafe {Self { neon: vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(self.neon), vreinterpretq_u32_f32(rhs.neon))) }}
484      } else {
485        Self { arr: [
486          f32::from_bits(self.arr[0].to_bits() & rhs.arr[0].to_bits()),
487          f32::from_bits(self.arr[1].to_bits() & rhs.arr[1].to_bits()),
488          f32::from_bits(self.arr[2].to_bits() & rhs.arr[2].to_bits()),
489          f32::from_bits(self.arr[3].to_bits() & rhs.arr[3].to_bits()),
490        ]}
491      }
492    }
493  }
494
495  #[inline]
496  fn bitor(self, rhs: Self) -> Self::Output {
497    pick! {
498      if #[cfg(target_feature="sse")] {
499        Self { sse: bitor_m128(self.sse, rhs.sse) }
500      } else if #[cfg(target_feature="simd128")] {
501        Self { simd: v128_or(self.simd, rhs.simd) }
502      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
503        unsafe {Self { neon: vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(self.neon), vreinterpretq_u32_f32(rhs.neon))) }}
504      } else {
505        Self { arr: [
506          f32::from_bits(self.arr[0].to_bits() | rhs.arr[0].to_bits()),
507          f32::from_bits(self.arr[1].to_bits() | rhs.arr[1].to_bits()),
508          f32::from_bits(self.arr[2].to_bits() | rhs.arr[2].to_bits()),
509          f32::from_bits(self.arr[3].to_bits() | rhs.arr[3].to_bits()),
510        ]}
511      }
512    }
513  }
514
515  #[inline]
516  fn bitxor(self, rhs: Self) -> Self::Output {
517    pick! {
518      if #[cfg(target_feature="sse")] {
519        Self { sse: bitxor_m128(self.sse, rhs.sse) }
520      } else if #[cfg(target_feature="simd128")] {
521        Self { simd: v128_xor(self.simd, rhs.simd) }
522      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
523        unsafe {Self { neon: vreinterpretq_f32_u32(veorq_u32(vreinterpretq_u32_f32(self.neon), vreinterpretq_u32_f32(rhs.neon))) }}
524      } else {
525        Self { arr: [
526          f32::from_bits(self.arr[0].to_bits() ^ rhs.arr[0].to_bits()),
527          f32::from_bits(self.arr[1].to_bits() ^ rhs.arr[1].to_bits()),
528          f32::from_bits(self.arr[2].to_bits() ^ rhs.arr[2].to_bits()),
529          f32::from_bits(self.arr[3].to_bits() ^ rhs.arr[3].to_bits()),
530        ]}
531      }
532    }
533  }
534
535  #[inline]
536  pub fn reduce_add(self) -> f32 {
537    let arr: [f32; 4] = cast(self);
538    arr.iter().sum()
539  }
540
541  #[inline]
542  pub fn reduce_mul(self) -> f32 {
543    let arr: [f32; 4] = cast(self);
544    arr.iter().product()
545  }
546
547  #[inline]
548  pub fn is_nan(self) -> Self {
549    pick! {
550      if #[cfg(target_feature="sse")] {
551        Self { sse: cmp_unord_mask_m128(self.sse, self.sse) }
552      } else if #[cfg(target_feature="simd128")] {
553        Self { simd: f32x4_ne(self.simd, self.simd) }
554      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
555        unsafe {Self { neon: vreinterpretq_f32_u32(vmvnq_u32(vceqq_f32(self.neon, self.neon))) }}
556      } else {
557        Self { arr: [
558          if self.arr[0].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
559          if self.arr[1].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
560          if self.arr[2].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
561          if self.arr[3].is_nan() { f32::from_bits(u32::MAX) } else { 0.0 },
562        ]}
563      }
564    }
565  }
566
567  #[inline]
568  pub fn is_inf(self) -> Self {
569    let shifted_inf = u32x4::from(0xFF000000);
570    let u: u32x4 = cast(self);
571    let shift_u = u << 1_u64;
572    let out = (shift_u).simd_eq(shifted_inf);
573    cast(out)
574  }
575
576  #[inline]
577  pub fn is_finite(self) -> Self {
578    let shifted_exp_mask = u32x4::from(0xFF000000);
579    let u: u32x4 = cast(self);
580    let shift_u = u << 1_u64;
581    let out = !(shift_u & shifted_exp_mask).simd_eq(shifted_exp_mask);
582    cast(out)
583  }
584
585  #[inline]
586  pub fn is_sign_positive(self) -> Self {
587    const SIGN_MASK: u32x4 = u32x4::splat((-0.0_f32).to_bits());
588
589    let bits = cast::<f32x4, u32x4>(self);
590    let sign = bits & SIGN_MASK;
591    let result = sign.simd_eq(u32x4::ZERO);
592    cast::<u32x4, f32x4>(result)
593  }
594
595  #[inline]
596  pub fn is_sign_negative(self) -> Self {
597    const SIGN_MASK: u32x4 = u32x4::splat((-0.0_f32).to_bits());
598
599    let bits = cast::<f32x4, u32x4>(self);
600    let sign = bits & SIGN_MASK;
601    let result = sign.simd_eq(SIGN_MASK);
602    cast::<u32x4, f32x4>(result)
603  }
604
605  #[inline]
606  pub fn recip(self) -> Self {
607    pick! {
608      if #[cfg(target_feature="sse")] {
609        Self { sse: reciprocal_m128(self.sse) }
610      } else if #[cfg(target_feature="simd128")] {
611        Self { simd: f32x4_div(f32x4_splat(1.0), self.simd) }
612      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
613        unsafe {Self { neon: vdivq_f32(vdupq_n_f32(1.0), self.neon) }}
614      } else {
615        Self { arr: [
616          1.0 / self.arr[0],
617          1.0 / self.arr[1],
618          1.0 / self.arr[2],
619          1.0 / self.arr[3],
620        ]}
621      }
622    }
623  }
624
625  #[inline]
626  pub fn recip_sqrt(self) -> Self {
627    pick! {
628      if #[cfg(target_feature="sse")] {
629        Self { sse: reciprocal_sqrt_m128(self.sse) }
630      } else if #[cfg(target_feature="simd128")] {
631        Self { simd: f32x4_div(f32x4_splat(1.0), f32x4_sqrt(self.simd)) }
632      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
633        unsafe {Self { neon: vdivq_f32(vdupq_n_f32(1.0), vsqrtq_f32(self.neon)) }}
634      } else if #[cfg(feature="std")] {
635        Self { arr: [
636          1.0 / self.arr[0].sqrt(),
637          1.0 / self.arr[1].sqrt(),
638          1.0 / self.arr[2].sqrt(),
639          1.0 / self.arr[3].sqrt(),
640        ]}
641      } else {
642        Self { arr: [
643          1.0 / software_sqrt(self.arr[0] as f64) as f32,
644          1.0 / software_sqrt(self.arr[1] as f64) as f32,
645          1.0 / software_sqrt(self.arr[2] as f64) as f32,
646          1.0 / software_sqrt(self.arr[3] as f64) as f32,
647        ]}
648      }
649    }
650  }
651
652  #[inline]
653  pub fn max(self, rhs: Self) -> Self {
654    pick! {
655      if #[cfg(target_feature="sse")] {
656        // max_m128 seems to do rhs < self ? self : rhs. So if there's any NaN
657        // involved, it chooses rhs, so we need to specifically check rhs for
658        // NaN.
659        rhs.is_nan().select(self, Self { sse: max_m128(self.sse, rhs.sse) })
660      } else if #[cfg(target_feature="simd128")] {
661        // WASM has two max intrinsics:
662        // - max: This propagates NaN, that's the opposite of what we need.
663        // - pmax: This is defined as self < rhs ? rhs : self, which basically
664        //   chooses self if either is NaN.
665        //
666        // pmax is what we want, but we need to specifically check self for NaN.
667        Self {
668          simd: v128_bitselect(
669            rhs.simd,
670            f32x4_pmax(self.simd, rhs.simd),
671            f32x4_ne(self.simd, self.simd), // NaN check
672          )
673        }
674      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
675        unsafe {Self { neon: vmaxnmq_f32(self.neon, rhs.neon) }}
676      } else {
677        Self { arr: [
678          self.arr[0].max(rhs.arr[0]),
679          self.arr[1].max(rhs.arr[1]),
680          self.arr[2].max(rhs.arr[2]),
681          self.arr[3].max(rhs.arr[3]),
682        ]}
683      }
684    }
685  }
686
687  #[inline]
688  pub fn fast_max(self, rhs: Self) -> Self {
689    pick! {
690      if #[cfg(target_feature="sse")] {
691        Self { sse: max_m128(self.sse, rhs.sse) }
692      } else if #[cfg(target_feature="simd128")] {
693        Self {
694          simd: f32x4_pmax(self.simd, rhs.simd),
695        }
696      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
697        unsafe {Self { neon: vmaxq_f32(self.neon, rhs.neon) }}
698      } else {
699        Self { arr: [
700          if self.arr[0] < rhs.arr[0] { rhs.arr[0] } else { self.arr[0] },
701          if self.arr[1] < rhs.arr[1] { rhs.arr[1] } else { self.arr[1] },
702          if self.arr[2] < rhs.arr[2] { rhs.arr[2] } else { self.arr[2] },
703          if self.arr[3] < rhs.arr[3] { rhs.arr[3] } else { self.arr[3] },
704        ]}
705      }
706    }
707  }
708
709  #[inline]
710  pub fn min(self, rhs: Self) -> Self {
711    pick! {
712      if #[cfg(target_feature="sse")] {
713        // min_m128 seems to do self < rhs ? self : rhs. So if there's any NaN
714        // involved, it chooses rhs, so we need to specifically check rhs for
715        // NaN.
716        rhs.is_nan().select(self, Self { sse: min_m128(self.sse, rhs.sse) })
717      } else if #[cfg(target_feature="simd128")] {
718        // WASM has two min intrinsics:
719        // - min: This propagates NaN, that's the opposite of what we need.
720        // - pmin: This is defined as rhs < self ? rhs : self, which basically
721        //   chooses self if either is NaN.
722        //
723        // pmin is what we want, but we need to specifically check self for NaN.
724        Self {
725          simd: v128_bitselect(
726            rhs.simd,
727            f32x4_pmin(self.simd, rhs.simd),
728            f32x4_ne(self.simd, self.simd), // NaN check
729          )
730        }
731      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
732        unsafe {Self { neon: vminnmq_f32(self.neon, rhs.neon) }}
733      } else {
734        Self { arr: [
735          self.arr[0].min(rhs.arr[0]),
736          self.arr[1].min(rhs.arr[1]),
737          self.arr[2].min(rhs.arr[2]),
738          self.arr[3].min(rhs.arr[3]),
739        ]}
740      }
741    }
742  }
743
744  #[inline]
745  pub fn fast_min(self, rhs: Self) -> Self {
746    pick! {
747      if #[cfg(target_feature="sse")] {
748        Self { sse: min_m128(self.sse, rhs.sse) }
749      } else if #[cfg(target_feature="simd128")] {
750        Self {
751          simd: f32x4_pmin(self.simd, rhs.simd),
752        }
753      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
754        unsafe {Self { neon: vminq_f32(self.neon, rhs.neon) }}
755      } else {
756        Self { arr: [
757          if self.arr[0] < rhs.arr[0] { self.arr[0] } else { rhs.arr[0] },
758          if self.arr[1] < rhs.arr[1] { self.arr[1] } else { rhs.arr[1] },
759          if self.arr[2] < rhs.arr[2] { self.arr[2] } else { rhs.arr[2] },
760          if self.arr[3] < rhs.arr[3] { self.arr[3] } else { rhs.arr[3] },
761        ]}
762      }
763    }
764  }
765
766  #[inline]
767  pub fn clamp(self, min: Self, max: Self) -> Self {
768    pick! {
769      if #[cfg(any(
770        target_feature="simd128",
771        all(target_feature="neon",target_arch="aarch64"),
772      ))] {
773        // `fast_clamp` already works.
774        self.fast_clamp(min, max)
775      } else {
776        // This works since all bits set is NaN.
777        self.fast_clamp(min, max) | min.is_nan() | max.is_nan()
778      }
779    }
780  }
781
782  #[inline]
783  pub fn fast_clamp(self, min: Self, max: Self) -> Self {
784    pick! {
785      if #[cfg(target_feature="sse")] {
786        // For both `min_m128` and `max_m128` if any input is NaN, `rhs` gets
787        // chosen. For `self` to be chosen, `self` must be the second argument.
788        Self { sse: max_m128(min.sse, min_m128(max.sse, self.sse)) }
789      } else if #[cfg(target_feature="simd128")] {
790        Self { simd: f32x4_max(f32x4_min(self.simd, max.simd), min.simd) }
791      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
792        unsafe { Self { neon: vmaxq_f32(vminq_f32(self.neon, max.neon), min.neon) } }
793      } else {
794        // The standard library does not have NaN propagating `min` and `max`
795        // functions.
796        let mut result = self;
797        result = result.simd_gt(max).select(max, result);
798        result = result.simd_lt(min).select(min, result);
799        result
800      }
801    }
802  }
803
804  #[inline]
805  pub fn abs(self) -> Self {
806    pick! {
807      if #[cfg(target_feature="simd128")] {
808        Self { simd: f32x4_abs(self.simd) }
809      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
810        unsafe {Self { neon: vabsq_f32(self.neon) }}
811      } else {
812        let non_sign_bits = f32x4::from(f32::from_bits(i32::MAX as u32));
813        self & non_sign_bits
814      }
815    }
816  }
817
818  #[inline]
819  pub fn floor(self) -> Self {
820    pick! {
821      if #[cfg(target_feature="simd128")] {
822        Self { simd: f32x4_floor(self.simd) }
823      } else if #[cfg(target_feature="sse4.1")] {
824        Self { sse: floor_m128(self.sse) }
825      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
826        unsafe {Self { neon: vrndmq_f32(self.neon) }}
827      } else if #[cfg(feature="std")] {
828        let base: [f32; 4] = cast(self);
829        cast(base.map(|val| val.floor()))
830      } else {
831        let base: [f32; 4] = cast(self);
832        let rounded: [f32; 4] = cast(self.round());
833        cast([
834          if base[0] < rounded[0] { rounded[0] - 1.0 } else { rounded[0] },
835          if base[1] < rounded[1] { rounded[1] - 1.0 } else { rounded[1] },
836          if base[2] < rounded[2] { rounded[2] - 1.0 } else { rounded[2] },
837          if base[3] < rounded[3] { rounded[3] - 1.0 } else { rounded[3] },
838        ])
839      }
840    }
841  }
842
843  #[inline]
844  pub fn ceil(self) -> Self {
845    pick! {
846      if #[cfg(target_feature="simd128")] {
847        Self { simd: f32x4_ceil(self.simd) }
848      } else if #[cfg(target_feature="sse4.1")] {
849        Self { sse: ceil_m128(self.sse) }
850      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
851        unsafe {Self { neon: vrndpq_f32(self.neon) }}
852      } else if #[cfg(feature="std")] {
853        let base: [f32; 4] = cast(self);
854        cast(base.map(|val| val.ceil()))
855      } else {
856        let base: [f32; 4] = cast(self);
857        let rounded: [f32; 4] = cast(self.round());
858        cast([
859          if base[0] > rounded[0] { rounded[0] + 1.0 } else { rounded[0] },
860          if base[1] > rounded[1] { rounded[1] + 1.0 } else { rounded[1] },
861          if base[2] > rounded[2] { rounded[2] + 1.0 } else { rounded[2] },
862          if base[3] > rounded[3] { rounded[3] + 1.0 } else { rounded[3] },
863        ])
864      }
865    }
866  }
867
868  #[inline]
869  pub fn round(self) -> Self {
870    pick! {
871      if #[cfg(target_feature="sse4.1")] {
872        const_f32_as_f32x4!(HALF_NEXT_DOWN, 0.5_f32.next_down());
873        const_f32_as_f32x4!(BOUNDS_LIMIT, 8388608.0);
874
875        let self_abs = self.abs();
876
877        let adjusted_self = self_abs + Self::HALF;
878        let result_abs = Self { sse: round_m128::<{round_op!(Zero)}>(adjusted_self.sse) };
879        // The addition breaks for `0.5.next_down()` which incorrectly rounds to
880        // `1.0`. This resets the result back to `0.0`.
881        let result_abs = result_abs & self_abs.simd_ne(HALF_NEXT_DOWN);
882
883        // Large value, infinity and NaN need special handling.
884        let bounds_mask: Self = cast(cmp_lt_mask_i32_m128i(cast(self_abs), cast(BOUNDS_LIMIT)));
885
886        // `abs` keeps the original sign.
887        bounds_mask.abs().bitselect(result_abs, self)
888      } else if #[cfg(target_feature="sse2")] {
889        const_f32_as_f32x4!(HALF_NEXT_DOWN, 0.5_f32.next_down());
890        const_f32_as_f32x4!(BOUNDS_LIMIT, 8388608.0);
891
892        let self_abs = self.abs();
893
894        let adjusted_self = self_abs + Self::HALF;
895        let result_abs = Self {
896          sse: convert_to_m128_from_i32_m128i(truncate_m128_to_m128i(adjusted_self.sse)),
897        };
898        // The addition breaks for `0.5.next_down()` which incorrectly rounds to
899        // `1.0`. This resets the result back to `0.0`.
900        let result_abs = result_abs & self_abs.simd_ne(HALF_NEXT_DOWN);
901
902        // Large value, infinity and NaN need special handling.
903        let bounds_mask: Self = cast(cmp_lt_mask_i32_m128i(cast(self_abs), cast(BOUNDS_LIMIT)));
904
905        // `abs` keeps the original sign.
906        bounds_mask.abs().bitselect(result_abs, self)
907      } else if #[cfg(target_feature="simd128")] {
908        const_f32_as_f32x4!(HALF_NEXT_DOWN, 0.5_f32.next_down());
909        const_f32_as_f32x4!(BOUNDS_LIMIT, 8388608.0);
910
911        let self_abs = self.abs();
912
913        let adjusted_self = self_abs + Self::HALF;
914        let result_abs = Self { simd: f32x4_trunc(adjusted_self.simd) };
915        // The addition breaks for `0.5.next_down()` which incorrectly rounds to
916        // `1.0`. This resets the result back to `0.0`.
917        let result_abs = result_abs & self_abs.simd_ne(HALF_NEXT_DOWN);
918
919        // Large value, infinity and NaN need special handling.
920        let bounds_mask = Self { simd: i32x4_lt(self_abs.simd, BOUNDS_LIMIT.simd) };
921
922        // `abs` keeps the original sign.
923        bounds_mask.abs().bitselect(result_abs, self)
924      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
925        unsafe {Self { neon: vrndaq_f32(self.neon) }}
926      } else {
927        const_f32_as_f32x4!(HALF_NEXT_DOWN, 0.5_f32.next_down());
928        const_f32_as_f32x4!(BOUNDS_LIMIT, 8388608.0);
929
930        let self_abs = self.abs();
931
932        let adjusted_self = (self_abs + Self::HALF).to_array();
933        let result_abs = Self::new([
934          adjusted_self[0] as u32 as f32,
935          adjusted_self[1] as u32 as f32,
936          adjusted_self[2] as u32 as f32,
937          adjusted_self[3] as u32 as f32,
938        ]);
939        // The addition breaks for `0.5.next_down()` which incorrectly rounds to
940        // `1.0`. This resets the result back to `0.0`.
941        let result_abs = result_abs & self_abs.simd_ne(HALF_NEXT_DOWN);
942
943        // Large value, infinity and NaN need special handling.
944        let bounds_mask: Self = cast(cast::<_, i32x4>(self_abs).simd_lt(cast::<_, i32x4>(BOUNDS_LIMIT)));
945
946        // `abs` keeps the original sign.
947        bounds_mask.abs().bitselect(result_abs, self)
948      }
949    }
950  }
951
952  #[inline]
953  pub fn round_int(self) -> i32x4 {
954    pick! {
955      if #[cfg(target_feature="sse2")] {
956        // Based on: https://github.com/v8/v8/blob/210987a552a2bf2a854b0baa9588a5959ff3979d/src/codegen/shared-ia32-x64/macro-assembler-shared-ia32-x64.h#L489-L504
957        let non_nan_mask = self.simd_eq(self);
958        let non_nan = self & non_nan_mask;
959        let flip_to_max: i32x4 = cast(self.simd_ge(Self::splat(2147483648.0)));
960        let cast: i32x4 = cast(convert_to_i32_m128i_from_m128(non_nan.sse));
961        flip_to_max ^ cast
962      } else if #[cfg(target_feature="simd128")] {
963        cast(Self { simd: i32x4_trunc_sat_f32x4(f32x4_nearest(self.simd)) })
964      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
965        cast(unsafe {Self { neon: vreinterpretq_f32_s32(vcvtnq_s32_f32(self.neon)) }})
966      } else {
967        let rounded: [f32; 4] = cast(self.round());
968        cast([
969          rounded[0] as i32,
970          rounded[1] as i32,
971          rounded[2] as i32,
972          rounded[3] as i32,
973        ])
974      }
975    }
976  }
977
978  #[inline]
979  pub fn fast_round_int(self) -> i32x4 {
980    pick! {
981      if #[cfg(target_feature="sse2")] {
982        cast(convert_to_i32_m128i_from_m128(self.sse))
983      } else {
984        self.round_int()
985      }
986    }
987  }
988
989  #[inline]
990  pub fn round_ties_even(self) -> Self {
991    pick! {
992      if #[cfg(target_feature="sse4.1")] {
993        Self { sse: round_m128::<{round_op!(Nearest)}>(self.sse) }
994      } else if #[cfg(target_feature="sse2")] {
995        let mi: m128i = convert_to_i32_m128i_from_m128(self.sse);
996        let f: f32x4 = f32x4 { sse: convert_to_m128_from_i32_m128i(mi) };
997        let i: i32x4 = cast(mi);
998        let mask: f32x4 = cast(i.simd_eq(i32x4::from(0x80000000_u32 as i32)));
999        mask.select(self, f)
1000      } else if #[cfg(target_feature="simd128")] {
1001        Self { simd: f32x4_nearest(self.simd) }
1002      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1003        unsafe {Self { neon: vrndnq_f32(self.neon) }}
1004      } else {
1005        // Note(Lokathor): This software fallback is probably very slow compared
1006        // to having a hardware option available, even just the sse2 version is
1007        // better than this. Oh well.
1008        let to_int = f32x4::from(1.0 / f32::EPSILON);
1009        let u: u32x4 = cast(self);
1010        let e: i32x4 = cast((u >> 23) & u32x4::from(0xff));
1011        let mut y: f32x4;
1012
1013        let no_op_magic = i32x4::from(0x7f + 23);
1014        let no_op_mask: f32x4 = cast(e.simd_gt(no_op_magic) | e.simd_eq(no_op_magic));
1015        let no_op_val: f32x4 = self;
1016
1017        let zero_magic = i32x4::from(0x7f - 1);
1018        let zero_mask: f32x4 = cast(e.simd_lt(zero_magic));
1019        let zero_val: f32x4 = self * f32x4::from(0.0);
1020
1021        let neg_bit: f32x4 = cast(cast::<u32x4, i32x4>(u).simd_lt(i32x4::default()));
1022        let x: f32x4 = neg_bit.select(-self, self);
1023        y = x + to_int - to_int - x;
1024        y = y.simd_gt(f32x4::from(0.5)).select(
1025          y + x - f32x4::from(-1.0),
1026          y.simd_lt(f32x4::from(-0.5)).select(y + x + f32x4::from(1.0), y + x),
1027        );
1028        y = neg_bit.select(-y, y);
1029
1030        no_op_mask.select(no_op_val, zero_mask.select(zero_val, y))
1031      }
1032    }
1033  }
1034
1035  #[inline]
1036  pub fn trunc(self) -> Self {
1037    pick! {
1038      if #[cfg(target_feature="sse4.1")] {
1039        Self { sse: round_m128::<{round_op!(Zero)}>(self.sse) }
1040      } else if #[cfg(target_feature="sse2")] {
1041        // Ported from https://docs.rs/glam/latest/glam/f32/struct.Vec4.html#method.trunc
1042        // Based on https://github.com/microsoft/DirectXMath `XMVectorTruncate`
1043        let result: Self = cast(convert_to_m128_from_i32_m128i(truncate_m128_to_m128i(self.sse)));
1044
1045        // Out of range values are either already round, infinite or NaN.
1046        let bounds_mask: Self = cast(cmp_lt_mask_i32_m128i(
1047            cast(self.abs()),
1048            set_splat_i32_m128i(8388608_f32.to_bits() as i32),
1049        ));
1050
1051        // Reset the sign bit of the mask to preverse the sign of `self`.
1052        bounds_mask.abs().select(result, self)
1053      } else if #[cfg(target_feature="simd128")] {
1054        Self { simd: f32x4_trunc(self.simd) }
1055      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1056        unsafe { Self { neon: vrndq_f32(self.neon) } }
1057      } else {
1058        let array: [f32; 4] = cast(self);
1059        let result: Self = cast([
1060          array[0] as i32 as f32,
1061          array[1] as i32 as f32,
1062          array[2] as i32 as f32,
1063          array[3] as i32 as f32,
1064        ]);
1065
1066        // Out of range values are either already round, infinite or NaN.
1067        const BOUNDS_LIMIT: i32 = 8388608_f32.to_bits() as i32;
1068        let bounds_mask: Self = cast(cast::<f32x4, i32x4>(self.abs()).simd_lt(i32x4::splat(BOUNDS_LIMIT)));
1069
1070        // Reset the sign bit of the mask to preverse the sign of `self`.
1071        bounds_mask.abs().select(result, self)
1072      }
1073    }
1074  }
1075
1076  #[inline]
1077  pub fn trunc_int(self) -> i32x4 {
1078    pick! {
1079      if #[cfg(target_feature="sse2")] {
1080        // Based on: https://github.com/v8/v8/blob/210987a552a2bf2a854b0baa9588a5959ff3979d/src/codegen/shared-ia32-x64/macro-assembler-shared-ia32-x64.h#L489-L504
1081        let non_nan_mask = self.simd_eq(self);
1082        let non_nan = self & non_nan_mask;
1083        let flip_to_max: i32x4 = cast(self.simd_ge(Self::splat(2147483648.0)));
1084        let cast: i32x4 = cast(truncate_m128_to_m128i(non_nan.sse));
1085        flip_to_max ^ cast
1086      } else if #[cfg(target_feature="simd128")] {
1087        cast(Self { simd: i32x4_trunc_sat_f32x4(self.simd) })
1088      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1089        cast(unsafe {Self { neon: vreinterpretq_f32_s32(vcvtq_s32_f32(self.neon)) }})
1090      } else {
1091        let n: [f32;4] = cast(self);
1092        cast([
1093          n[0] as i32,
1094          n[1] as i32,
1095          n[2] as i32,
1096          n[3] as i32,
1097        ])
1098      }
1099    }
1100  }
1101
1102  #[inline]
1103  pub fn fast_trunc_int(self) -> i32x4 {
1104    pick! {
1105      if #[cfg(target_feature="sse2")] {
1106        cast(truncate_m128_to_m128i(self.sse))
1107      } else {
1108        self.trunc_int()
1109      }
1110    }
1111  }
1112
1113  ///
1114  /// # Platform-specific behavior (may change in the future)
1115  ///
1116  /// - On `x86`/`x86_64` with FMA: Uses `vfmadd` (single rounding, best
1117  ///   accuracy)
1118  /// - On ARM64 with NEON: Uses `vfmaq_f32` (single rounding, best accuracy)
1119  /// - Without FMA support: Uses `(self * m) + a` (two roundings)
1120  #[inline]
1121  pub fn mul_add(self, a: Self, b: Self) -> Self {
1122    pick! {
1123      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1124        Self { sse: fused_mul_add_m128(self.sse, a.sse, b.sse) }
1125      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1126        unsafe { Self { neon: vfmaq_f32(b.neon, self.neon, a.neon) } }
1127      } else {
1128        (self * a) + b
1129      }
1130    }
1131  }
1132
1133  ///
1134  /// # Platform-specific behavior (may change in the future)
1135  ///
1136  /// - On `x86`/`x86_64` with FMA: Uses `vfmsub` (single rounding, best
1137  ///   accuracy)
1138  /// - On ARM64 with NEON: Uses `vfmaq_f32(-s, self, m)` (single rounding, best
1139  ///   accuracy)
1140  /// - Without FMA support: Uses `(self * m) - s` (two roundings)
1141  #[inline]
1142  pub fn mul_sub(self, a: Self, b: Self) -> Self {
1143    pick! {
1144      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1145        Self { sse: fused_mul_sub_m128(self.sse, a.sse, b.sse) }
1146      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1147        unsafe { Self { neon: vfmaq_f32(vnegq_f32(b.neon), self.neon, a.neon) } }
1148      } else {
1149        (self * a) - b
1150      }
1151    }
1152  }
1153
1154  ///
1155  /// # Platform-specific behavior (may change in the future)
1156  ///
1157  /// - On `x86`/`x86_64` with FMA: Uses `vfnmadd` (single rounding, best
1158  ///   accuracy)
1159  /// - On ARM64 with NEON: Uses `vfmsq_f32` (single rounding, best accuracy)
1160  /// - Without FMA support: Uses `a - (self * m)` (two roundings)
1161  #[inline]
1162  pub fn mul_neg_add(self, a: Self, b: Self) -> Self {
1163    pick! {
1164      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1165        Self { sse: fused_mul_neg_add_m128(self.sse, a.sse, b.sse) }
1166      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1167        unsafe { Self { neon: vfmsq_f32(b.neon, self.neon, a.neon) } }
1168      } else {
1169        b - (self * a)
1170      }
1171    }
1172  }
1173
1174  ///
1175  /// # Platform-specific behavior (may change in the future)
1176  ///
1177  /// - On `x86`/`x86_64` with FMA: Uses `vfnmsub` (single rounding, best
1178  ///   accuracy)
1179  /// - On ARM64 with NEON: Uses `-(vfmaq_f32(s, self, m))` (single rounding,
1180  ///   best accuracy)
1181  /// - Without FMA support: Uses `-(self * m) - s` (two roundings)
1182  #[inline]
1183  pub fn mul_neg_sub(self, a: Self, b: Self) -> Self {
1184    pick! {
1185      if #[cfg(all(target_feature="sse2",target_feature="fma"))] {
1186        Self { sse: fused_mul_neg_sub_m128(self.sse, a.sse, b.sse) }
1187      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
1188        unsafe { Self { neon: vnegq_f32(vfmaq_f32(b.neon, self.neon, a.neon)) } }
1189      } else {
1190        -(self * a) - b
1191      }
1192    }
1193  }
1194
1195  #[inline]
1196  pub fn powf_simd(self, n: Self) -> Self {
1197    const_f32_as_f32x4!(ln2f_hi, 0.693359375);
1198    const_f32_as_f32x4!(ln2f_lo, -2.12194440e-4);
1199    const_f32_as_f32x4!(P0logf, 3.3333331174E-1);
1200    const_f32_as_f32x4!(P1logf, -2.4999993993E-1);
1201    const_f32_as_f32x4!(P2logf, 2.0000714765E-1);
1202    const_f32_as_f32x4!(P3logf, -1.6668057665E-1);
1203    const_f32_as_f32x4!(P4logf, 1.4249322787E-1);
1204    const_f32_as_f32x4!(P5logf, -1.2420140846E-1);
1205    const_f32_as_f32x4!(P6logf, 1.1676998740E-1);
1206    const_f32_as_f32x4!(P7logf, -1.1514610310E-1);
1207    const_f32_as_f32x4!(P8logf, 7.0376836292E-2);
1208
1209    const_f32_as_f32x4!(p2expf, 1.0 / 2.0); // coefficients for Taylor expansion of exp
1210    const_f32_as_f32x4!(p3expf, 1.0 / 6.0);
1211    const_f32_as_f32x4!(p4expf, 1.0 / 24.0);
1212    const_f32_as_f32x4!(p5expf, 1.0 / 120.0);
1213    const_f32_as_f32x4!(p6expf, 1.0 / 720.0);
1214    const_f32_as_f32x4!(p7expf, 1.0 / 5040.0);
1215
1216    let x1 = self.abs();
1217    let x = x1.fraction_2();
1218
1219    let mask = x.simd_gt(f32x4::SQRT_2 * f32x4::HALF);
1220    let x = (!mask).select(x + x, x);
1221
1222    let x = x - f32x4::ONE;
1223    let x2 = x * x;
1224    let lg1 = polynomial_8!(
1225      x, P0logf, P1logf, P2logf, P3logf, P4logf, P5logf, P6logf, P7logf, P8logf
1226    );
1227    let lg1 = lg1 * x2 * x;
1228
1229    let ef = x1.exponent();
1230    let ef = mask.select(ef + f32x4::ONE, ef);
1231
1232    let e1 = (ef * n).round_ties_even();
1233    let yr = ef.mul_sub(n, e1);
1234
1235    let lg = f32x4::HALF.mul_neg_add(x2, x) + lg1;
1236    let x2_err = (f32x4::HALF * x).mul_sub(x, f32x4::HALF * x2);
1237    let lg_err = f32x4::HALF.mul_add(x2, lg - x) - lg1;
1238
1239    let e2 = (lg * n * f32x4::LOG2_E).round_ties_even();
1240    let v = lg.mul_sub(n, e2 * ln2f_hi);
1241    let v = e2.mul_neg_add(ln2f_lo, v);
1242    let v = v - (lg_err + x2_err).mul_sub(n, yr * f32x4::LN_2);
1243
1244    let x = v;
1245    let e3 = (x * f32x4::LOG2_E).round_ties_even();
1246    let x = e3.mul_neg_add(f32x4::LN_2, x);
1247    let x2 = x * x;
1248    let z = x2.mul_add(
1249      polynomial_5!(x, p2expf, p3expf, p4expf, p5expf, p6expf, p7expf),
1250      x + f32x4::ONE,
1251    );
1252
1253    let ee = e1 + e2 + e3;
1254    let ei = cast::<_, i32x4>(ee.round_int());
1255    let ej = cast::<_, i32x4>(ei + (cast::<_, i32x4>(z) >> 23));
1256
1257    let overflow = cast::<_, f32x4>(ej.simd_gt(i32x4::splat(0x0FF)))
1258      | (ee.simd_gt(f32x4::splat(300.0)));
1259    let underflow = cast::<_, f32x4>(ej.simd_lt(i32x4::splat(0x000)))
1260      | (ee.simd_lt(f32x4::splat(-300.0)));
1261
1262    // Add exponent by integer addition
1263    let z = cast::<_, f32x4>(cast::<_, i32x4>(z) + (ei << 23));
1264
1265    // Check for overflow/underflow
1266    let z = if (overflow | underflow).any() {
1267      let z = underflow.select(f32x4::ZERO, z);
1268      overflow.select(Self::infinity(), z)
1269    } else {
1270      z
1271    };
1272
1273    // Check for self == 0
1274    let x_zero = self.is_zero_or_subnormal();
1275    let z = x_zero.select(
1276      n.simd_lt(f32x4::ZERO).select(
1277        Self::infinity(),
1278        n.simd_eq(f32x4::ZERO).select(f32x4::ONE, f32x4::ZERO),
1279      ),
1280      z,
1281    );
1282
1283    let x_sign = self.is_sign_negative();
1284    let z = if x_sign.any() {
1285      // Y into an integer
1286      let yi = n.simd_eq(n.round_ties_even());
1287      // Is y odd? If yes flip the sign of the result.
1288      let y_odd = cast::<i32x4, f32x4>(n.round_int() << 31);
1289
1290      let z1 = yi
1291        .select(z | y_odd, self.simd_eq(Self::ZERO).select(z, Self::nan_pow()));
1292      x_sign.select(z1, z)
1293    } else {
1294      z
1295    };
1296
1297    let x_finite = self.is_finite();
1298    let y_finite = n.is_finite();
1299    let e_finite = ee.is_finite();
1300    if (x_finite & y_finite & (e_finite | x_zero)).all() {
1301      return z;
1302    }
1303
1304    (self.is_nan() | n.is_nan()).select(self + n, z)
1305  }
1306
1307  #[inline]
1308  pub fn sqrt(self) -> Self {
1309    pick! {
1310      if #[cfg(target_feature="sse")] {
1311        Self { sse: sqrt_m128(self.sse) }
1312      } else if #[cfg(target_feature="simd128")] {
1313        Self { simd: f32x4_sqrt(self.simd) }
1314      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
1315        unsafe {Self { neon: vsqrtq_f32(self.neon) }}
1316      } else if #[cfg(feature="std")] {
1317        Self { arr: [
1318          self.arr[0].sqrt(),
1319          self.arr[1].sqrt(),
1320          self.arr[2].sqrt(),
1321          self.arr[3].sqrt(),
1322        ]}
1323      } else {
1324        Self { arr: [
1325          software_sqrt(self.arr[0] as f64) as f32,
1326          software_sqrt(self.arr[1] as f64) as f32,
1327          software_sqrt(self.arr[2] as f64) as f32,
1328          software_sqrt(self.arr[3] as f64) as f32,
1329        ]}
1330      }
1331    }
1332  }
1333
1334  /// Calculate the exponent of a packed `f32x4`
1335  #[inline]
1336  pub fn exp(self) -> Self {
1337    const_f32_as_f32x4!(P0, 1.0 / 2.0);
1338    const_f32_as_f32x4!(P1, 1.0 / 6.0);
1339    const_f32_as_f32x4!(P2, 1.0 / 24.0);
1340    const_f32_as_f32x4!(P3, 1.0 / 120.0);
1341    const_f32_as_f32x4!(P4, 1.0 / 720.0);
1342    const_f32_as_f32x4!(P5, 1.0 / 5040.0);
1343    // LN2D_HI/LO: double-double decomposition of ln(2) for exp range reduction,
1344    // following the approach from fdlibm's e_exp.c (Sun Microsystems,
1345    // https://www.netlib.org/fdlibm/). The f32 split uses f32-precision constants
1346    // (0.693359375, -2.12194440e-4) summing to ln(2) with single-precision
1347    // accuracy; the f64 variants use a full f64 double-double
1348    // decomposition.
1349    const_f32_as_f32x4!(LN2D_HI, 0.693359375);
1350    const_f32_as_f32x4!(LN2D_LO, -2.12194440e-4);
1351    // max_x = ln(f32::MAX) ≈ 88.7229, max_r = 127 (IEEE max normal exponent)
1352    // min_x = -149.5 ln(2) ≈ -103.63: min r for vm_pow2n subnormal
1353    let max_x = f32x4::from(88.723);
1354    let min_x = f32x4::from(-103.63);
1355    // x < min_x: e^x underflows to 0 -- skip the entire pipeline
1356    let finite = self.is_finite();
1357    let neg_underflow = self.simd_lt(min_x) & finite;
1358    if neg_underflow.all() {
1359      return Self::ZERO;
1360    }
1361    let max_r = f32x4::from(127.0);
1362    let r = (self * Self::LOG2_E).round_ties_even();
1363    let big = r.simd_gt(max_r);
1364    let r_safe = big.select(max_r, r);
1365    let excess = r - max_r;
1366    let excess = big.select(excess, Self::ZERO);
1367    let scale = Self::vm_pow2n(excess);
1368    let x = r.mul_neg_add(LN2D_HI, self);
1369    let x = r.mul_neg_add(LN2D_LO, x);
1370    let z = polynomial_5!(x, P0, P1, P2, P3, P4, P5);
1371    let x2 = x * x;
1372    let z = z.mul_add(x2, x);
1373    let n2 = Self::vm_pow2n(r_safe);
1374    let z = (z + Self::ONE) * scale * n2;
1375    let nan_mask = self.is_nan();
1376    let mut result = nan_mask.select(Self::nan_pow(), z);
1377    let pos_overflow = self.simd_gt(max_x) & finite;
1378    result = pos_overflow.select(Self::infinity(), result);
1379    result = neg_underflow.select(Self::ZERO, result);
1380    let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1381    result = pos_inf.select(Self::infinity(), result);
1382    let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1383    result = neg_inf.select(Self::ZERO, result);
1384    result
1385  }
1386
1387  #[inline]
1388  pub fn exp2(self) -> Self {
1389    const_f32_as_f32x4!(P2, 1.0 / 2.0);
1390    const_f32_as_f32x4!(P3, 1.0 / 6.0);
1391    const_f32_as_f32x4!(P4, 1.0 / 24.0);
1392    const_f32_as_f32x4!(P5, 1.0 / 120.0);
1393    const_f32_as_f32x4!(P6, 1.0 / 720.0);
1394    const_f32_as_f32x4!(P7, 1.0 / 5040.0);
1395
1396    // max_x = log2(f32::MAX) ≈ 127.99999
1397    // min_x = log2(f32::MIN_POSITIVE) - 23 ≈ -126 - 23 = -149
1398    let max_x = f32x4::from(127.99999);
1399    let min_x = f32x4::from(-149.5);
1400    let finite = self.is_finite();
1401    let neg_underflow = self.simd_lt(min_x) & finite;
1402    if neg_underflow.all() {
1403      return Self::ZERO;
1404    }
1405
1406    let round = self.round_ties_even();
1407    let max_r = f32x4::from(127.0);
1408    let big = round.simd_gt(max_r);
1409    let r_safe = big.select(max_r, round);
1410    let excess = round - max_r;
1411    let excess = big.select(excess, Self::ZERO);
1412    let scale = Self::vm_pow2n(excess);
1413
1414    let fract = (self - round) * Self::LN_2;
1415    let fract_partial_exp2 = polynomial_5!(fract, P2, P3, P4, P5, P6, P7);
1416    let fract2 = fract * fract;
1417    let fract_exp2 = fract_partial_exp2.mul_add(fract2, fract) + Self::ONE;
1418
1419    let n2 = Self::vm_pow2n(r_safe);
1420    let result = fract_exp2 * scale * n2;
1421
1422    let nan_mask = self.is_nan();
1423    let mut result = nan_mask.select(Self::nan_pow(), result);
1424    let pos_overflow = self.simd_gt(max_x) & finite;
1425    result = pos_overflow.select(Self::infinity(), result);
1426    result = neg_underflow.select(Self::ZERO, result);
1427    let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1428    result = pos_inf.select(Self::infinity(), result);
1429    let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1430    result = neg_inf.select(Self::ZERO, result);
1431    result
1432  }
1433
1434  #[inline]
1435  pub fn ln(self) -> Self {
1436    const_f32_as_f32x4!(HALF, 0.5);
1437    const_f32_as_f32x4!(P0, 3.3333331174E-1);
1438    const_f32_as_f32x4!(P1, -2.4999993993E-1);
1439    const_f32_as_f32x4!(P2, 2.0000714765E-1);
1440    const_f32_as_f32x4!(P3, -1.6668057665E-1);
1441    const_f32_as_f32x4!(P4, 1.4249322787E-1);
1442    const_f32_as_f32x4!(P5, -1.2420140846E-1);
1443    const_f32_as_f32x4!(P6, 1.1676998740E-1);
1444    const_f32_as_f32x4!(P7, -1.1514610310E-1);
1445    const_f32_as_f32x4!(P8, 7.0376836292E-2);
1446    const_f32_as_f32x4!(LN2F_HI, 0.693359375);
1447    const_f32_as_f32x4!(LN2F_LO, -2.12194440e-4);
1448    const_f32_as_f32x4!(VM_SMALLEST_NORMAL, 1.17549435E-38);
1449
1450    let x1 = self;
1451    let x = Self::fraction_2(x1);
1452    let e = Self::exponent(x1);
1453    let mask = x.simd_gt(Self::SQRT_2 * HALF);
1454    let x = (!mask).select(x + x, x);
1455    let fe = mask.select(e + Self::ONE, e);
1456    let x = x - Self::ONE;
1457    let res = polynomial_8!(x, P0, P1, P2, P3, P4, P5, P6, P7, P8);
1458    let x2 = x * x;
1459    let res = x2 * x * res;
1460    let res = fe.mul_add(LN2F_LO, res);
1461    let res = res + x2.mul_neg_add(HALF, x);
1462    let res = fe.mul_add(LN2F_HI, res);
1463    let overflow = !self.is_finite();
1464    let underflow = x1.simd_lt(VM_SMALLEST_NORMAL);
1465    let mask = overflow | underflow;
1466    if !mask.any() {
1467      res
1468    } else {
1469      let is_zero = self.is_zero_or_subnormal();
1470      let res = underflow.select(Self::nan_log(), res);
1471      // Note: is_zero_or_subnormal() lumps subnormals (exponent==0) with zero.
1472      // Both get -Inf here. True subnormal inputs (~1.4e-45..1.175e-38) should
1473      // produce a finite negative result, but are vanishingly rare in
1474      // practice.
1475      let res = is_zero.select(-Self::infinity(), res);
1476      let res = overflow.select(self, res);
1477      // This must come *after* overflow.blend to overwrite ln(-∞) = -∞ to NaN
1478      let res = (!self.is_finite() & self.is_sign_negative())
1479        .select(Self::nan_log(), res);
1480      res
1481    }
1482  }
1483
1484  #[inline]
1485  pub fn cbrt(self) -> Self {
1486    let a = self.abs();
1487    let zero = a.simd_eq(Self::ZERO);
1488    if zero.all() {
1489      return self; // preserves -0.0
1490    }
1491    let inf = a.is_inf();
1492    let nan = self.is_nan();
1493
1494    let tiny = a.simd_lt(Self::from(f32::MIN_POSITIVE));
1495    let a_work = tiny.select(a * Self::from(16777216.0), a);
1496
1497    let e = Self::exponent(a_work) + Self::ONE;
1498    let d = Self::fraction_2(a_work);
1499
1500    // C0..C5 from SLEEF's minimax polynomial for 1/cbrt(d) on [0.5, 1.0)
1501    // Naoki Shibata et al., "SLEEF: A Portable Vectorized Library of C99
1502    // Mathematical Functions", https://sleef.org / https://github.com/shibatch/sleef
1503    // Licensed under the Boost Software License 1.0.
1504    // These are the f32-precision coefficients; our f64 variants use the f64
1505    // set.
1506    const_f32_as_f32x4!(C0, 2.2241257);
1507    const_f32_as_f32x4!(C1, -3.8095417);
1508    const_f32_as_f32x4!(C2, 5.8982625);
1509    const_f32_as_f32x4!(C3, -5.532182);
1510    const_f32_as_f32x4!(C4, 2.8208892);
1511    const_f32_as_f32x4!(C5, -0.60156447);
1512    let mut x = polynomial_5!(d, C0, C1, C2, C3, C4, C5);
1513
1514    let x2 = x * x;
1515    let x4 = x2 * x2;
1516    x = x - d.mul_add(x4, -x) * Self::from(1.0 / 3.0);
1517    // cbrt(d) = d * x² with refinement
1518    let mut y = (d * x) * x;
1519    let yx = y * x;
1520    let t = Self::from(2.0 / 3.0);
1521    y = y - t * y * (yx - Self::ONE);
1522
1523    // Scale by 2^(e/3)
1524    let three = Self::from(3.0);
1525    let two = Self::from(2.0);
1526    let neg = e.simd_lt(Self::ZERO);
1527    let e_adj = neg.select(e - two, e);
1528    let k = (e_adj / three).trunc();
1529    let r = e - three * k;
1530    const_f32_as_f32x4!(CBRT2, 1.259921);
1531    const_f32_as_f32x4!(CBRT4, 1.587401);
1532    y = r.simd_eq(Self::ONE).select(y * CBRT2, y);
1533    y = r.simd_eq(two).select(y * CBRT4, y);
1534    y *= Self::vm_pow2n(k);
1535    y = tiny.select(y / Self::from(256.0_f32), y);
1536
1537    let result = y.flip_signs(self);
1538    let result = nan.select(self, result);
1539    let result = zero.select(self, result);
1540    let result = inf.select(self, result);
1541    result
1542  }
1543
1544  #[inline]
1545  pub fn asin(self) -> Self {
1546    // Based on the Agner Fog "vector class library":
1547    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1548    const_f32_as_f32x4!(P4asinf, 4.2163199048E-2);
1549    const_f32_as_f32x4!(P3asinf, 2.4181311049E-2);
1550    const_f32_as_f32x4!(P2asinf, 4.5470025998E-2);
1551    const_f32_as_f32x4!(P1asinf, 7.4953002686E-2);
1552    const_f32_as_f32x4!(P0asinf, 1.6666752422E-1);
1553
1554    let xa = self.abs();
1555    let big = xa.simd_ge(f32x4::splat(0.5));
1556
1557    let x1 = f32x4::splat(0.5) * (f32x4::ONE - xa);
1558    let x2 = xa * xa;
1559    let x3 = big.select(x1, x2);
1560
1561    let xb = x1.sqrt();
1562
1563    let x4 = big.select(xb, xa);
1564
1565    let z = polynomial_4!(x3, P0asinf, P1asinf, P2asinf, P3asinf, P4asinf);
1566    let z = z.mul_add(x3 * x4, x4);
1567
1568    let z1 = z + z;
1569
1570    // asin
1571    let z3 = f32x4::FRAC_PI_2 - z1;
1572    let asin = big.select(z3, z);
1573    let asin = asin.flip_signs(self);
1574
1575    asin
1576  }
1577
1578  #[inline]
1579  pub fn acos(self) -> Self {
1580    // Based on the Agner Fog "vector class library":
1581    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1582    const_f32_as_f32x4!(P4asinf, 4.2163199048E-2);
1583    const_f32_as_f32x4!(P3asinf, 2.4181311049E-2);
1584    const_f32_as_f32x4!(P2asinf, 4.5470025998E-2);
1585    const_f32_as_f32x4!(P1asinf, 7.4953002686E-2);
1586    const_f32_as_f32x4!(P0asinf, 1.6666752422E-1);
1587
1588    let xa = self.abs();
1589    let big = xa.simd_ge(f32x4::splat(0.5));
1590
1591    let x1 = f32x4::splat(0.5) * (f32x4::ONE - xa);
1592    let x2 = xa * xa;
1593    let x3 = big.select(x1, x2);
1594
1595    let xb = x1.sqrt();
1596
1597    let x4 = big.select(xb, xa);
1598
1599    let z = polynomial_4!(x3, P0asinf, P1asinf, P2asinf, P3asinf, P4asinf);
1600    let z = z.mul_add(x3 * x4, x4);
1601
1602    let z1 = z + z;
1603
1604    // acos
1605    let z3 = self.simd_lt(f32x4::ZERO).select(f32x4::PI - z1, z1);
1606    let z4 = f32x4::FRAC_PI_2 - z.flip_signs(self);
1607    let acos = big.select(z3, z4);
1608
1609    acos
1610  }
1611
1612  #[inline]
1613  pub fn atan(self) -> Self {
1614    // Based on the Agner Fog "vector class library":
1615    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1616    const_f32_as_f32x4!(P3atanf, 8.05374449538E-2);
1617    const_f32_as_f32x4!(P2atanf, -1.38776856032E-1);
1618    const_f32_as_f32x4!(P1atanf, 1.99777106478E-1);
1619    const_f32_as_f32x4!(P0atanf, -3.33329491539E-1);
1620
1621    let t = self.abs();
1622
1623    // small:  z = t / 1.0;
1624    // medium: z = (t-1.0) / (t+1.0);
1625    // big:    z = -1.0 / t;
1626    let notsmal = t.simd_ge(Self::SQRT_2 - Self::ONE);
1627    let notbig = t.simd_le(Self::SQRT_2 + Self::ONE);
1628
1629    let mut s = notbig.select(Self::FRAC_PI_4, Self::FRAC_PI_2);
1630    s = notsmal & s;
1631
1632    let mut a = notbig & t;
1633    a = notsmal.select(a - Self::ONE, a);
1634    let mut b = notbig & Self::ONE;
1635    b = notsmal.select(b + t, b);
1636    let z = a / b;
1637
1638    let zz = z * z;
1639
1640    // Taylor expansion
1641    let mut re = polynomial_3!(zz, P0atanf, P1atanf, P2atanf, P3atanf);
1642    re = re.mul_add(zz * z, z) + s;
1643
1644    // get sign bit
1645    re = (self.is_sign_negative()).select(-re, re);
1646
1647    re
1648  }
1649
1650  #[inline]
1651  pub fn atan2(self, x: Self) -> Self {
1652    // Based on the Agner Fog "vector class library":
1653    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1654    const_f32_as_f32x4!(P3atanf, 8.05374449538E-2);
1655    const_f32_as_f32x4!(P2atanf, -1.38776856032E-1);
1656    const_f32_as_f32x4!(P1atanf, 1.99777106478E-1);
1657    const_f32_as_f32x4!(P0atanf, -3.33329491539E-1);
1658
1659    let y = self;
1660
1661    // move in first octant
1662    let x1 = x.abs();
1663    let y1 = y.abs();
1664    let swapxy = y1.simd_gt(x1);
1665    // swap x and y if y1 > x1
1666    let mut x2 = swapxy.select(y1, x1);
1667    let mut y2 = swapxy.select(x1, y1);
1668
1669    // check for special case: x and y are both +/- INF
1670    let both_infinite = x.is_inf() & y.is_inf();
1671    if both_infinite.any() {
1672      let minus_one = -Self::ONE;
1673      x2 = both_infinite.select(x2 & minus_one, x2);
1674      y2 = both_infinite.select(y2 & minus_one, y2);
1675    }
1676
1677    // x = y = 0 will produce NAN. No problem, fixed below
1678    let t = y2 / x2;
1679
1680    // small:  z = t / 1.0;
1681    // medium: z = (t-1.0) / (t+1.0);
1682    let notsmal = t.simd_ge(Self::SQRT_2 - Self::ONE);
1683
1684    let a = notsmal.select(t - Self::ONE, t);
1685    let b = notsmal.select(t + Self::ONE, Self::ONE);
1686    let s = notsmal & Self::FRAC_PI_4;
1687    let z = a / b;
1688
1689    let zz = z * z;
1690
1691    // Taylor expansion
1692    let mut re = polynomial_3!(zz, P0atanf, P1atanf, P2atanf, P3atanf);
1693    re = re.mul_add(zz * z, z) + s;
1694
1695    // move back in place
1696    re = swapxy.select(Self::FRAC_PI_2 - re, re);
1697    re = ((x | y).simd_eq(Self::ZERO)).select(Self::ZERO, re);
1698    re = (x.is_sign_negative()).select(Self::PI - re, re);
1699
1700    // get sign bit
1701    re = (y.is_sign_negative()).select(-re, re);
1702
1703    re
1704  }
1705
1706  #[inline]
1707  pub fn sin_cos(self) -> (Self, Self) {
1708    // Based on the Agner Fog "vector class library":
1709    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1710
1711    const_f32_as_f32x4!(DP1F, 0.78515625_f32 * 2.0);
1712    const_f32_as_f32x4!(DP2F, 2.4187564849853515625E-4_f32 * 2.0);
1713    const_f32_as_f32x4!(DP3F, 3.77489497744594108E-8_f32 * 2.0);
1714
1715    const_f32_as_f32x4!(P0sinf, -1.6666654611E-1);
1716    const_f32_as_f32x4!(P1sinf, 8.3321608736E-3);
1717    const_f32_as_f32x4!(P2sinf, -1.9515295891E-4);
1718
1719    const_f32_as_f32x4!(P0cosf, 4.166664568298827E-2);
1720    const_f32_as_f32x4!(P1cosf, -1.388731625493765E-3);
1721    const_f32_as_f32x4!(P2cosf, 2.443315711809948E-5);
1722
1723    const_f32_as_f32x4!(TWO_OVER_PI, 2.0 / core::f32::consts::PI);
1724
1725    let xa = self.abs();
1726
1727    // Find quadrant
1728    let y = (xa * TWO_OVER_PI).round_ties_even();
1729    let q: i32x4 = y.round_int();
1730
1731    let x = y.mul_neg_add(DP3F, y.mul_neg_add(DP2F, y.mul_neg_add(DP1F, xa)));
1732
1733    let x2 = x * x;
1734    let mut s = polynomial_2!(x2, P0sinf, P1sinf, P2sinf) * (x * x2) + x;
1735    let mut c = polynomial_2!(x2, P0cosf, P1cosf, P2cosf) * (x2 * x2)
1736      + f32x4::from(0.5).mul_neg_add(x2, f32x4::from(1.0));
1737
1738    let swap = !(q & i32x4::from(1)).simd_eq(i32x4::from(0));
1739
1740    let mut overflow: f32x4 = cast(q.simd_gt(i32x4::from(0x2000000)));
1741    overflow &= xa.is_finite();
1742    s = overflow.select(f32x4::from(0.0), s);
1743    c = overflow.select(f32x4::from(1.0), c);
1744
1745    // calc sin
1746    let mut sin1 = cast::<_, f32x4>(swap).select(c, s);
1747    let sign_sin: i32x4 = (q << 30) ^ cast::<_, i32x4>(self);
1748    sin1 = sin1.flip_signs(cast(sign_sin));
1749
1750    // calc cos
1751    let mut cos1 = cast::<_, f32x4>(swap).select(s, c);
1752    let sign_cos: i32x4 = ((q + i32x4::from(1)) & i32x4::from(2)) << 30;
1753    cos1 ^= cast::<_, f32x4>(sign_cos);
1754
1755    // IEEE 754: sin/cos(±∞) = NaN, sin/cos(NaN) = NaN
1756    let finite = self.is_finite();
1757    let nan = Self::splat(f32::NAN);
1758    let sin_final = finite.select(sin1, nan);
1759    let cos_final = finite.select(cos1, nan);
1760
1761    (sin_final, cos_final)
1762  }
1763
1764  #[inline]
1765  pub fn asin_acos(self) -> (Self, Self) {
1766    // Based on the Agner Fog "vector class library":
1767    // https://github.com/vectorclass/version2/blob/master/vectormath_trig.h
1768    const_f32_as_f32x4!(P4asinf, 4.2163199048E-2);
1769    const_f32_as_f32x4!(P3asinf, 2.4181311049E-2);
1770    const_f32_as_f32x4!(P2asinf, 4.5470025998E-2);
1771    const_f32_as_f32x4!(P1asinf, 7.4953002686E-2);
1772    const_f32_as_f32x4!(P0asinf, 1.6666752422E-1);
1773
1774    let xa = self.abs();
1775    let big = xa.simd_ge(f32x4::splat(0.5));
1776
1777    let x1 = f32x4::splat(0.5) * (f32x4::ONE - xa);
1778    let x2 = xa * xa;
1779    let x3 = big.select(x1, x2);
1780
1781    let xb = x1.sqrt();
1782
1783    let x4 = big.select(xb, xa);
1784
1785    let z = polynomial_4!(x3, P0asinf, P1asinf, P2asinf, P3asinf, P4asinf);
1786    let z = z.mul_add(x3 * x4, x4);
1787
1788    let z1 = z + z;
1789
1790    // acos
1791    let z3 = self.simd_lt(f32x4::ZERO).select(f32x4::PI - z1, z1);
1792    let z4 = f32x4::FRAC_PI_2 - z.flip_signs(self);
1793    let acos = big.select(z3, z4);
1794
1795    // asin
1796    let z3 = f32x4::FRAC_PI_2 - z1;
1797    let asin = big.select(z3, z);
1798    let asin = asin.flip_signs(self);
1799
1800    (asin, acos)
1801  }
1802
1803  #[inline]
1804  pub fn exp_m1(self) -> Self {
1805    // x < -17.329: e^x < 2⁻²⁵, exp_m1(x) = -1.0 exactly (mantissa exhaustion)
1806    // IEEE simd_lt returns false for NaN, so NaN lanes can't reach here.
1807    // -inf is < -17.329, and exp_m1(-inf) = -1.0, also correct.
1808    if self.simd_lt(f32x4::from(-17.329)).all() {
1809      return f32x4::from(-1.0);
1810    }
1811    const_f32_as_f32x4!(P0, 1.0 / 2.0);
1812    const_f32_as_f32x4!(P1, 1.0 / 6.0);
1813    const_f32_as_f32x4!(P2, 1.0 / 24.0);
1814    const_f32_as_f32x4!(P3, 1.0 / 120.0);
1815    const_f32_as_f32x4!(P4, 1.0 / 720.0);
1816    const_f32_as_f32x4!(P5, 1.0 / 5040.0);
1817    // LN2D_HI/LO: double-double decomposition of ln(2) for exp range reduction,
1818    // following the approach from fdlibm's e_exp.c (Sun Microsystems,
1819    // https://www.netlib.org/fdlibm/). The f32 split uses f32-precision constants
1820    // (0.693359375, -2.12194440e-4) summing to ln(2) with single-precision
1821    // accuracy; the f64 variants use a full f64 double-double
1822    // decomposition.
1823    const_f32_as_f32x4!(LN2D_HI, 0.693359375);
1824    const_f32_as_f32x4!(LN2D_LO, -2.12194440e-4);
1825    // max_x = ln(f32::MAX) ≈ 88.7229, max_r = 127 (IEEE max normal exponent)
1826    // min_x = -149.5 ln(2) ≈ -103.63: min r for vm_pow2n subnormal
1827    let max_x = f32x4::from(88.723);
1828    let min_x = f32x4::from(-103.63);
1829    let max_r = f32x4::from(127.0);
1830    let r = (self * Self::LOG2_E).round_ties_even();
1831    let big = r.simd_gt(max_r);
1832    let r_safe = big.select(max_r, r);
1833    let excess = r - max_r;
1834    let excess = big.select(excess, Self::ZERO);
1835    let scale = Self::vm_pow2n(excess);
1836    let x = r.mul_neg_add(LN2D_HI, self);
1837    let x = r.mul_neg_add(LN2D_LO, x);
1838    let z = polynomial_5!(x, P0, P1, P2, P3, P4, P5);
1839    let x2 = x * x;
1840    let z = z.mul_add(x2, x);
1841    let n2 = Self::vm_pow2n(r_safe);
1842    let exp_val = (z + Self::ONE) * scale * n2;
1843    let r_is_zero = r.simd_eq(Self::ZERO);
1844    let z = r_is_zero.select(z, exp_val - Self::ONE);
1845    let nan_mask = self.is_nan();
1846    let finite = self.is_finite();
1847    let mut result = nan_mask.select(Self::nan_pow(), z);
1848    let pos_overflow = self.simd_gt(max_x) & finite;
1849    result = pos_overflow.select(Self::infinity(), result);
1850    let neg_underflow = self.simd_lt(min_x) & finite;
1851    result = neg_underflow.select(-Self::ONE, result);
1852    let pos_inf = !finite & !self.is_sign_negative() & !nan_mask;
1853    result = pos_inf.select(Self::infinity(), result);
1854    let neg_inf = !finite & self.is_sign_negative() & !nan_mask;
1855    result = neg_inf.select(-Self::ONE, result);
1856    let is_zero = self.simd_eq(Self::ZERO);
1857    result = is_zero.select(self, result);
1858    result
1859  }
1860
1861  #[inline]
1862  pub fn ln_1p(self) -> Self {
1863    // Based on the identity ln(1+x) = x·ln(1+x)/((1+x)-1), i.e. x·ln(u)/(u-1)
1864    // where u = 1+x. From MUSL libc (Rich Felker et al., https://musl.libc.org) src/math/log1pf.c
1865    // and fdlibm (Sun Microsystems, https://www.netlib.org/fdlibm/) s_log1p.c.
1866    // When 1+x rounds to 1 exactly (subnormal x), return x directly.
1867    // When 1+x overflows (+inf), return ln(u) without correction.
1868    // Mathematically exact: compensates for the rounding loss in 1+x without
1869    // needing a series threshold.
1870    let u = self + Self::ONE;
1871    let eq = u.simd_eq(Self::ONE);
1872    let ln_u = Self::ln(u);
1873    let correction = self * (ln_u / (u - Self::ONE));
1874    let result = eq.select(self, correction);
1875    let over = u.is_inf();
1876    over.select(ln_u, result)
1877  }
1878
1879  #[inline]
1880  pub fn sinh(self) -> Self {
1881    const_f32_as_f32x4!(P0, 1.0);
1882    const_f32_as_f32x4!(P1, 1.0 / 6.0);
1883    const_f32_as_f32x4!(P2, 1.0 / 120.0);
1884    const_f32_as_f32x4!(P3, 1.0 / 5040.0);
1885    let a = self.abs();
1886    // |x| < 0.5: Taylor poly; last truncation term < 1 ULP at x=0.5 for both types
1887    let small = a.simd_lt(f32x4::from(0.5));
1888    let t = a * a;
1889    let poly = a * polynomial_3!(t, P0, P1, P2, P3);
1890    let exp_based = {
1891      let e = a.exp();
1892      (e - Self::ONE / e) * Self::HALF
1893    };
1894    let result = small.select(poly, exp_based);
1895    result.flip_signs(self)
1896  }
1897
1898  #[inline]
1899  pub fn cosh(self) -> Self {
1900    const_f32_as_f32x4!(P0, 1.0);
1901    const_f32_as_f32x4!(P1, 1.0 / 2.0);
1902    const_f32_as_f32x4!(P2, 1.0 / 24.0);
1903    const_f32_as_f32x4!(P3, 1.0 / 720.0);
1904    let a = self.abs();
1905    // |x| < 0.5: Taylor poly; last truncation term < 1 ULP at x=0.5 for both types
1906    let small = a.simd_lt(f32x4::from(0.5));
1907    let t = a * a;
1908    let poly = polynomial_3!(t, P0, P1, P2, P3);
1909    let exp_based = {
1910      let e = a.exp();
1911      (e + Self::ONE / e) * Self::HALF
1912    };
1913    small.select(poly, exp_based)
1914  }
1915
1916  #[inline]
1917  pub fn tanh(self) -> Self {
1918    // |x| < 2e-4: tanh(x) ≈ x, error x³/3 < 16·ULP(x)
1919    // bound: x² < 48·2⁻²³ → x < 2.39e-3; 2e-4 has 10× margin
1920    // |x| > 9.011: tanh(x) = ±1 to f32 precision (e⁻²ˣ < 2⁻²⁴)
1921    let a = self.abs();
1922    let large = a.simd_gt(f32x4::from(9.011));
1923    if large.all() {
1924      return Self::ONE.flip_signs(self);
1925    }
1926    let small = a.simd_lt(f32x4::from(2e-4));
1927    let exp_based = {
1928      let t = (Self::from(-2.0) * a).exp_m1();
1929      let pos = -t / (t + Self::from(2.0));
1930      pos.flip_signs(self)
1931    };
1932    let result = small.select(self, exp_based);
1933    large.select(Self::ONE.flip_signs(self), result)
1934  }
1935}
1936
1937/// The following functionality exists only for [`f32x4`], or only for
1938/// particular types inconsistently.
1939impl f32x4 {
1940  #[inline]
1941  fn vm_pow2n(self) -> Self {
1942    const_f32_as_f32x4!(pow2_23, 8388608.0);
1943    const_f32_as_f32x4!(bias, 127.0);
1944    let a = self + (bias + pow2_23);
1945    let c = cast::<_, i32x4>(a) << 23;
1946    let std_result = cast::<_, f32x4>(c);
1947
1948    let min_exp = f32x4::from(-126.0);
1949    let is_sub = self.simd_lt(min_exp);
1950    if is_sub.any() {
1951      let valid = self.simd_ge(f32x4::from(-149.0));
1952      let shift_f = self + f32x4::from(149.0);
1953      let mut shift_i = shift_f.trunc_int();
1954      shift_i = cast::<_, i32x4>(valid).select(shift_i, i32x4::ZERO);
1955      let mantissa = i32x4::ONE << shift_i;
1956      let sub_result = cast::<_, f32x4>(mantissa);
1957      let sub_result = valid.select(sub_result, f32x4::ZERO);
1958      is_sub.select(sub_result, std_result)
1959    } else {
1960      std_result
1961    }
1962  }
1963
1964  #[inline]
1965  fn exponent(self) -> f32x4 {
1966    const_f32_as_f32x4!(pow2_23, 8388608.0);
1967    const_f32_as_f32x4!(bias, 127.0);
1968    let a = cast::<_, u32x4>(self);
1969    let b = a >> 23;
1970    let c = b | cast::<_, u32x4>(pow2_23);
1971    let d = cast::<_, f32x4>(c);
1972    let e = d - (pow2_23 + bias);
1973    e
1974  }
1975
1976  #[inline]
1977  fn fraction_2(self) -> Self {
1978    let t1 = cast::<_, u32x4>(self);
1979    let t2 = cast::<_, u32x4>(
1980      (t1 & u32x4::from(0x007FFFFF)) | u32x4::from(0x3F000000),
1981    );
1982    cast::<_, f32x4>(t2)
1983  }
1984  #[inline]
1985  fn is_zero_or_subnormal(self) -> Self {
1986    let t = cast::<_, i32x4>(self);
1987    let t = t & i32x4::splat(0x7F800000);
1988    let mask = t.simd_eq(i32x4::splat(0));
1989    cast::<_, f32x4>(mask)
1990  }
1991  #[inline]
1992  fn infinity() -> Self {
1993    cast::<_, f32x4>(i32x4::splat(0x7F800000))
1994  }
1995  #[inline]
1996  fn nan_log() -> Self {
1997    cast::<_, f32x4>(i32x4::splat(0x7FC00000 | 0x101 & 0x003FFFFF))
1998  }
1999  #[inline]
2000  fn nan_pow() -> Self {
2001    cast::<_, f32x4>(i32x4::splat(0x7FC00000 | 0x101 & 0x003FFFFF))
2002  }
2003
2004  /// Returns `[self[0], b[0], self[1], b[1]]`.
2005  #[must_use]
2006  #[inline]
2007  pub fn unpack_lo(self, b: Self) -> Self {
2008    pick! {
2009      if #[cfg(target_feature="sse")] {
2010        Self { sse: unpack_low_m128(self.sse, b.sse) }
2011      } else if #[cfg(target_feature="simd128")] {
2012        Self {
2013          simd: u32x4_shuffle::<0, 4, 1, 5>(self.simd, b.simd)
2014        }
2015      } else if #[cfg(all(target_feature="neon", target_arch="aarch64"))]{
2016        unsafe {Self { neon: vzip1q_f32(self.neon, b.neon) }}
2017      } else {
2018        Self { arr: [
2019          self.arr[0],
2020          b.arr[0],
2021          self.arr[1],
2022          b.arr[1],
2023        ]}
2024      }
2025    }
2026  }
2027
2028  /// Returns `[self[2], b[2], self[3], b[3]]`.
2029  #[must_use]
2030  #[inline]
2031  pub fn unpack_hi(self, b: Self) -> Self {
2032    pick! {
2033      if #[cfg(target_feature="sse")] {
2034        Self { sse: unpack_high_m128(self.sse, b.sse) }
2035      } else if #[cfg(target_feature="simd128")] {
2036        Self {
2037          simd: u32x4_shuffle::<2, 6, 3, 7>(self.simd, b.simd)
2038        }
2039      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))]{
2040        unsafe {Self { neon: vzip2q_f32(self.neon, b.neon) }}
2041      } else {
2042        Self { arr: [
2043          self.arr[2],
2044          b.arr[2],
2045          self.arr[3],
2046          b.arr[3],
2047        ]}
2048      }
2049    }
2050  }
2051
2052  /// Converts each element from [`i32`] to [`f32`].
2053  #[inline]
2054  pub fn from_i32x4(v: i32x4) -> Self {
2055    pick! {
2056      if #[cfg(target_feature="sse2")] {
2057        Self { sse: convert_to_m128_from_i32_m128i(v.sse) }
2058      } else if #[cfg(target_feature="simd128")] {
2059        Self { simd: f32x4_convert_i32x4(v.simd) }
2060      } else if #[cfg(all(target_feature="neon",target_arch="aarch64"))] {
2061        Self { neon: unsafe { vcvtq_f32_s32(v.neon) }}
2062      } else {
2063        Self { arr: [
2064            v.as_array()[0] as f32,
2065            v.as_array()[1] as f32,
2066            v.as_array()[2] as f32,
2067            v.as_array()[3] as f32,
2068          ] }
2069      }
2070    }
2071  }
2072
2073  /// Returns a [mask] that checks if each element has a negative sign,
2074  /// including `-0.0`, NaNs with negative sign bit and negative infinity.
2075  ///
2076  /// Note that this function has a misleading name. If the sign bit is set, the
2077  /// result has all bits set, not just the sign bit. This function has been
2078  /// renamed to [`is_sign_negative`].
2079  ///
2080  /// [mask]: crate#masks
2081  /// [`is_sign_negative`]: Self::is_sign_negative
2082  #[inline]
2083  #[must_use]
2084  #[deprecated(since = "1.4.0", note = "renamed to `is_sign_negative`")]
2085  pub fn sign_bit(self) -> Self {
2086    self.is_sign_negative()
2087  }
2088}