std/portable-simd/crates/std_float/src/
lib.rs

1#![cfg_attr(
2    feature = "as_crate",
3    feature(core_intrinsics),
4    feature(portable_simd),
5    allow(internal_features)
6)]
7#[cfg(not(feature = "as_crate"))]
8use core::simd;
9#[cfg(feature = "as_crate")]
10use core_simd::simd;
11
12use core::intrinsics::simd as intrinsics;
13
14use simd::{LaneCount, Simd, SupportedLaneCount};
15
16#[cfg(feature = "as_crate")]
17mod experimental {
18    pub trait Sealed {}
19}
20
21#[cfg(feature = "as_crate")]
22use experimental as sealed;
23
24use crate::sealed::Sealed;
25
26/// This trait provides a possibly-temporary implementation of float functions
27/// that may, in the absence of hardware support, canonicalize to calling an
28/// operating system's `math.h` dynamically-loaded library (also known as a
29/// shared object). As these conditionally require runtime support, they
30/// should only appear in binaries built assuming OS support: `std`.
31///
32/// However, there is no reason SIMD types, in general, need OS support,
33/// as for many architectures an embedded binary may simply configure that
34/// support itself. This means these types must be visible in `core`
35/// but have these functions available in `std`.
36///
37/// [`f32`] and [`f64`] achieve a similar trick by using "lang items", but
38/// due to compiler limitations, it is harder to implement this approach for
39/// abstract data types like [`Simd`]. From that need, this trait is born.
40///
41/// It is possible this trait will be replaced in some manner in the future,
42/// when either the compiler or its supporting runtime functions are improved.
43/// For now this trait is available to permit experimentation with SIMD float
44/// operations that may lack hardware support, such as `mul_add`.
45pub trait StdFloat: Sealed + Sized {
46    /// Elementwise fused multiply-add. Computes `(self * a) + b` with only one rounding error,
47    /// yielding a more accurate result than an unfused multiply-add.
48    ///
49    /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target
50    /// architecture has a dedicated `fma` CPU instruction.  However, this is not always
51    /// true, and will be heavily dependent on designing algorithms with specific target
52    /// hardware in mind.
53    #[inline]
54    #[must_use = "method returns a new vector and does not mutate the original value"]
55    fn mul_add(self, a: Self, b: Self) -> Self {
56        unsafe { intrinsics::simd_fma(self, a, b) }
57    }
58
59    /// Produces a vector where every element has the square root value
60    /// of the equivalently-indexed element in `self`
61    #[inline]
62    #[must_use = "method returns a new vector and does not mutate the original value"]
63    fn sqrt(self) -> Self {
64        unsafe { intrinsics::simd_fsqrt(self) }
65    }
66
67    /// Produces a vector where every element has the sine of the value
68    /// in the equivalently-indexed element in `self`.
69    #[must_use = "method returns a new vector and does not mutate the original value"]
70    fn sin(self) -> Self;
71
72    /// Produces a vector where every element has the cosine of the value
73    /// in the equivalently-indexed element in `self`.
74    #[must_use = "method returns a new vector and does not mutate the original value"]
75    fn cos(self) -> Self;
76
77    /// Produces a vector where every element has the exponential (base e) of the value
78    /// in the equivalently-indexed element in `self`.
79    #[must_use = "method returns a new vector and does not mutate the original value"]
80    fn exp(self) -> Self;
81
82    /// Produces a vector where every element has the exponential (base 2) of the value
83    /// in the equivalently-indexed element in `self`.
84    #[must_use = "method returns a new vector and does not mutate the original value"]
85    fn exp2(self) -> Self;
86
87    /// Produces a vector where every element has the natural logarithm of the value
88    /// in the equivalently-indexed element in `self`.
89    #[must_use = "method returns a new vector and does not mutate the original value"]
90    fn ln(self) -> Self;
91
92    /// Produces a vector where every element has the logarithm with respect to an arbitrary
93    /// in the equivalently-indexed elements in `self` and `base`.
94    #[inline]
95    #[must_use = "method returns a new vector and does not mutate the original value"]
96    fn log(self, base: Self) -> Self {
97        unsafe { intrinsics::simd_div(self.ln(), base.ln()) }
98    }
99
100    /// Produces a vector where every element has the base-2 logarithm of the value
101    /// in the equivalently-indexed element in `self`.
102    #[must_use = "method returns a new vector and does not mutate the original value"]
103    fn log2(self) -> Self;
104
105    /// Produces a vector where every element has the base-10 logarithm of the value
106    /// in the equivalently-indexed element in `self`.
107    #[must_use = "method returns a new vector and does not mutate the original value"]
108    fn log10(self) -> Self;
109
110    /// Returns the smallest integer greater than or equal to each element.
111    #[must_use = "method returns a new vector and does not mutate the original value"]
112    #[inline]
113    fn ceil(self) -> Self {
114        unsafe { intrinsics::simd_ceil(self) }
115    }
116
117    /// Returns the largest integer value less than or equal to each element.
118    #[must_use = "method returns a new vector and does not mutate the original value"]
119    #[inline]
120    fn floor(self) -> Self {
121        unsafe { intrinsics::simd_floor(self) }
122    }
123
124    /// Rounds to the nearest integer value. Ties round toward zero.
125    #[must_use = "method returns a new vector and does not mutate the original value"]
126    #[inline]
127    fn round(self) -> Self {
128        unsafe { intrinsics::simd_round(self) }
129    }
130
131    /// Returns the floating point's integer value, with its fractional part removed.
132    #[must_use = "method returns a new vector and does not mutate the original value"]
133    #[inline]
134    fn trunc(self) -> Self {
135        unsafe { intrinsics::simd_trunc(self) }
136    }
137
138    /// Returns the floating point's fractional value, with its integer part removed.
139    #[must_use = "method returns a new vector and does not mutate the original value"]
140    fn fract(self) -> Self;
141}
142
143impl<const N: usize> Sealed for Simd<f32, N> where LaneCount<N>: SupportedLaneCount {}
144impl<const N: usize> Sealed for Simd<f64, N> where LaneCount<N>: SupportedLaneCount {}
145
146macro_rules! impl_float {
147    {
148        $($fn:ident: $intrinsic:ident,)*
149    } => {
150        impl<const N: usize> StdFloat for Simd<f32, N>
151        where
152            LaneCount<N>: SupportedLaneCount,
153        {
154            #[inline]
155            fn fract(self) -> Self {
156                self - self.trunc()
157            }
158
159            $(
160            #[inline]
161            fn $fn(self) -> Self {
162                unsafe { intrinsics::$intrinsic(self) }
163            }
164            )*
165        }
166
167        impl<const N: usize> StdFloat for Simd<f64, N>
168        where
169            LaneCount<N>: SupportedLaneCount,
170        {
171            #[inline]
172            fn fract(self) -> Self {
173                self - self.trunc()
174            }
175
176            $(
177            #[inline]
178            fn $fn(self) -> Self {
179                // https://github.com/llvm/llvm-project/issues/83729
180                #[cfg(target_arch = "aarch64")]
181                {
182                    let mut ln = Self::splat(0f64);
183                    for i in 0..N {
184                        ln[i] = self[i].$fn()
185                    }
186                    ln
187                }
188
189                #[cfg(not(target_arch = "aarch64"))]
190                {
191                    unsafe { intrinsics::$intrinsic(self) }
192                }
193            }
194            )*
195        }
196    }
197}
198
199impl_float! {
200    sin: simd_fsin,
201    cos: simd_fcos,
202    exp: simd_fexp,
203    exp2: simd_fexp2,
204    ln: simd_flog,
205    log2: simd_flog2,
206    log10: simd_flog10,
207}