1 #![cfg_attr(feature = "as_crate", no_std)] // We are std!
2 #![cfg_attr(feature = "as_crate", feature(platform_intrinsics), feature(portable_simd))]
3 #[cfg(not(feature = "as_crate"))]
5 #[cfg(feature = "as_crate")]
8 use simd::{LaneCount, Simd, SupportedLaneCount};
10 #[cfg(feature = "as_crate")]
15 #[cfg(feature = "as_crate")]
16 use experimental as sealed;
18 use crate::sealed::Sealed;
20 // "platform intrinsics" are essentially "codegen intrinsics"
21 // each of these may be scalarized and lowered to a libm call
22 extern "platform-intrinsic" {
24 fn simd_ceil<T>(x: T) -> T;
27 fn simd_floor<T>(x: T) -> T;
30 fn simd_round<T>(x: T) -> T;
33 fn simd_trunc<T>(x: T) -> T;
36 fn simd_fsqrt<T>(x: T) -> T;
39 fn simd_fma<T>(x: T, y: T, z: T) -> T;
42 /// This trait provides a possibly-temporary implementation of float functions
43 /// that may, in the absence of hardware support, canonicalize to calling an
44 /// operating system's `math.h` dynamically-loaded library (also known as a
45 /// shared object). As these conditionally require runtime support, they
46 /// should only appear in binaries built assuming OS support: `std`.
48 /// However, there is no reason SIMD types, in general, need OS support,
49 /// as for many architectures an embedded binary may simply configure that
50 /// support itself. This means these types must be visible in `core`
51 /// but have these functions available in `std`.
53 /// [`f32`] and [`f64`] achieve a similar trick by using "lang items", but
54 /// due to compiler limitations, it is harder to implement this approach for
55 /// abstract data types like [`Simd`]. From that need, this trait is born.
57 /// It is possible this trait will be replaced in some manner in the future,
58 /// when either the compiler or its supporting runtime functions are improved.
59 /// For now this trait is available to permit experimentation with SIMD float
60 /// operations that may lack hardware support, such as `mul_add`.
61 pub trait StdFloat: Sealed + Sized {
62 /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error,
63 /// yielding a more accurate result than an unfused multiply-add.
65 /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target
66 /// architecture has a dedicated `fma` CPU instruction. However, this is not always
67 /// true, and will be heavily dependent on designing algorithms with specific target
70 #[must_use = "method returns a new vector and does not mutate the original value"]
71 fn mul_add(self, a: Self, b: Self) -> Self {
72 unsafe { simd_fma(self, a, b) }
75 /// Produces a vector where every lane has the square root value
76 /// of the equivalently-indexed lane in `self`
78 #[must_use = "method returns a new vector and does not mutate the original value"]
79 fn sqrt(self) -> Self {
80 unsafe { simd_fsqrt(self) }
83 /// Returns the smallest integer greater than or equal to each lane.
84 #[must_use = "method returns a new vector and does not mutate the original value"]
86 fn ceil(self) -> Self {
87 unsafe { simd_ceil(self) }
90 /// Returns the largest integer value less than or equal to each lane.
91 #[must_use = "method returns a new vector and does not mutate the original value"]
93 fn floor(self) -> Self {
94 unsafe { simd_floor(self) }
97 /// Rounds to the nearest integer value. Ties round toward zero.
98 #[must_use = "method returns a new vector and does not mutate the original value"]
100 fn round(self) -> Self {
101 unsafe { simd_round(self) }
104 /// Returns the floating point's integer value, with its fractional part removed.
105 #[must_use = "method returns a new vector and does not mutate the original value"]
107 fn trunc(self) -> Self {
108 unsafe { simd_trunc(self) }
111 /// Returns the floating point's fractional value, with its integer part removed.
112 #[must_use = "method returns a new vector and does not mutate the original value"]
113 fn fract(self) -> Self;
116 impl<const N: usize> Sealed for Simd<f32, N> where LaneCount<N>: SupportedLaneCount {}
117 impl<const N: usize> Sealed for Simd<f64, N> where LaneCount<N>: SupportedLaneCount {}
119 // We can safely just use all the defaults.
120 impl<const N: usize> StdFloat for Simd<f32, N>
122 LaneCount<N>: SupportedLaneCount,
124 /// Returns the floating point's fractional value, with its integer part removed.
125 #[must_use = "method returns a new vector and does not mutate the original value"]
127 fn fract(self) -> Self {
132 impl<const N: usize> StdFloat for Simd<f64, N>
134 LaneCount<N>: SupportedLaneCount,
136 /// Returns the floating point's fractional value, with its integer part removed.
137 #[must_use = "method returns a new vector and does not mutate the original value"]
139 fn fract(self) -> Self {
150 fn everything_works() {
151 let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]);
157 let _xfma = x.mul_add(x, x);
158 let _xsqrt = x.sqrt();
159 let _ = x2.abs() * x2;