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