std_float/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::Simd;
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 #[inline]
70 #[must_use = "method returns a new vector and does not mutate the original value"]
71 fn sin(self) -> Self {
72 unsafe { intrinsics::simd_fsin(self) }
73 }
74
75 /// Produces a vector where every element has the cosine of the value
76 /// in the equivalently-indexed element in `self`.
77 #[inline]
78 #[must_use = "method returns a new vector and does not mutate the original value"]
79 fn cos(self) -> Self {
80 unsafe { intrinsics::simd_fcos(self) }
81 }
82
83 /// Produces a vector where every element has the exponential (base e) of the value
84 /// in the equivalently-indexed element in `self`.
85 #[inline]
86 #[must_use = "method returns a new vector and does not mutate the original value"]
87 fn exp(self) -> Self {
88 unsafe { intrinsics::simd_fexp(self) }
89 }
90
91 /// Produces a vector where every element has the exponential (base 2) of the value
92 /// in the equivalently-indexed element in `self`.
93 #[inline]
94 #[must_use = "method returns a new vector and does not mutate the original value"]
95 fn exp2(self) -> Self {
96 unsafe { intrinsics::simd_fexp2(self) }
97 }
98
99 /// Produces a vector where every element has the natural logarithm of the value
100 /// in the equivalently-indexed element in `self`.
101 #[inline]
102 #[must_use = "method returns a new vector and does not mutate the original value"]
103 fn ln(self) -> Self {
104 unsafe { intrinsics::simd_flog(self) }
105 }
106
107 /// Produces a vector where every element has the logarithm with respect to an arbitrary
108 /// in the equivalently-indexed elements in `self` and `base`.
109 #[inline]
110 #[must_use = "method returns a new vector and does not mutate the original value"]
111 fn log(self, base: Self) -> Self {
112 unsafe { intrinsics::simd_div(self.ln(), base.ln()) }
113 }
114
115 /// Produces a vector where every element has the base-2 logarithm of the value
116 /// in the equivalently-indexed element in `self`.
117 #[inline]
118 #[must_use = "method returns a new vector and does not mutate the original value"]
119 fn log2(self) -> Self {
120 unsafe { intrinsics::simd_flog2(self) }
121 }
122
123 /// Produces a vector where every element has the base-10 logarithm of the value
124 /// in the equivalently-indexed element in `self`.
125 #[inline]
126 #[must_use = "method returns a new vector and does not mutate the original value"]
127 fn log10(self) -> Self {
128 unsafe { intrinsics::simd_flog10(self) }
129 }
130
131 /// Returns the smallest integer greater than or equal to each element.
132 #[must_use = "method returns a new vector and does not mutate the original value"]
133 #[inline]
134 fn ceil(self) -> Self {
135 unsafe { intrinsics::simd_ceil(self) }
136 }
137
138 /// Returns the largest integer value less than or equal to each element.
139 #[must_use = "method returns a new vector and does not mutate the original value"]
140 #[inline]
141 fn floor(self) -> Self {
142 unsafe { intrinsics::simd_floor(self) }
143 }
144
145 /// Rounds to the nearest integer value. Ties round toward zero.
146 #[must_use = "method returns a new vector and does not mutate the original value"]
147 #[inline]
148 fn round(self) -> Self {
149 unsafe { intrinsics::simd_round(self) }
150 }
151
152 /// Returns the floating point's integer value, with its fractional part removed.
153 #[must_use = "method returns a new vector and does not mutate the original value"]
154 #[inline]
155 fn trunc(self) -> Self {
156 unsafe { intrinsics::simd_trunc(self) }
157 }
158
159 /// Returns the floating point's fractional value, with its integer part removed.
160 #[must_use = "method returns a new vector and does not mutate the original value"]
161 fn fract(self) -> Self;
162}
163
164impl<const N: usize> Sealed for Simd<f32, N> {}
165impl<const N: usize> Sealed for Simd<f64, N> {}
166
167impl<const N: usize> StdFloat for Simd<f32, N> {
168 #[inline]
169 fn fract(self) -> Self {
170 self - self.trunc()
171 }
172}
173
174impl<const N: usize> StdFloat for Simd<f64, N> {
175 #[inline]
176 fn fract(self) -> Self {
177 self - self.trunc()
178 }
179}