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}