Skip to main content

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