decimal_scaled/trig_fast.rs
1//! Lossy (f64-bridge) trig methods for D38.
2//!
3//! Companion to `trig_strict.rs`. The plain methods here are the
4//! f64-bridge variants, gated on std + (no strict feature or
5//! fast set). When strict is on, the dispatcher in the
6//! _strict file shadows these.
7
8use crate::core_type::D38;
9
10impl<const SCALE: u32> D38<SCALE> {
11 // ── Forward trig (radians input) ──────────────────────────────────
12
13 /// Sine of `self`, where `self` is in radians.
14 ///
15 /// # Precision
16 ///
17 /// Lossy: involves f64 at some point; result may lose precision.
18 ///
19 /// # Examples
20 ///
21 /// ```ignore
22 /// # #[cfg(feature = "std")]
23 /// # {
24 /// use decimal_scaled::D38s12;
25 /// // sin(0) == 0 (bit-exact: f64::sin(0.0) == 0.0).
26 /// assert_eq!(D38s12::ZERO.sin(), D38s12::ZERO);
27 /// # }
28 /// ```
29 #[cfg(feature = "std")]
30 #[inline]
31 #[must_use]
32 pub fn sin_fast(self) -> Self {
33 Self::from_f64(self.to_f64().sin())
34 }
35
36 /// Cosine of `self`, where `self` is in radians.
37 ///
38 /// # Precision
39 ///
40 /// Lossy: involves f64 at some point; result may lose precision.
41 ///
42 /// # Examples
43 ///
44 /// ```ignore
45 /// # #[cfg(feature = "std")]
46 /// # {
47 /// use decimal_scaled::D38s12;
48 /// // cos(0) == 1 (bit-exact: f64::cos(0.0) == 1.0).
49 /// assert_eq!(D38s12::ZERO.cos(), D38s12::ONE);
50 /// # }
51 /// ```
52 #[cfg(feature = "std")]
53 #[inline]
54 #[must_use]
55 pub fn cos_fast(self) -> Self {
56 Self::from_f64(self.to_f64().cos())
57 }
58
59 /// Tangent of `self`, where `self` is in radians.
60 ///
61 /// `f64::tan` returns very large magnitudes near odd multiples of
62 /// `pi/2` and infinity at the limit. Inputs that drive the f64
63 /// result outside `[D38::MIN, D38::MAX]` saturate per
64 /// [`Self::from_f64`].
65 ///
66 /// # Precision
67 ///
68 /// Lossy: involves f64 at some point; result may lose precision.
69 ///
70 /// # Examples
71 ///
72 /// ```ignore
73 /// # #[cfg(feature = "std")]
74 /// # {
75 /// use decimal_scaled::D38s12;
76 /// // tan(0) == 0 (bit-exact: f64::tan(0.0) == 0.0).
77 /// assert_eq!(D38s12::ZERO.tan(), D38s12::ZERO);
78 /// # }
79 /// ```
80 #[cfg(feature = "std")]
81 #[inline]
82 #[must_use]
83 pub fn tan_fast(self) -> Self {
84 Self::from_f64(self.to_f64().tan())
85 }
86
87 // ── Inverse trig (returns radians) ────────────────────────────────
88
89 /// Arcsine of `self`. Returns radians in `[-pi/2, pi/2]`.
90 ///
91 /// `f64::asin` returns NaN for inputs outside `[-1, 1]`, which
92 /// [`Self::from_f64`] maps to `D38::ZERO`.
93 ///
94 /// # Precision
95 ///
96 /// Lossy: involves f64 at some point; result may lose precision.
97 ///
98 /// # Examples
99 ///
100 /// ```ignore
101 /// # #[cfg(feature = "std")]
102 /// # {
103 /// use decimal_scaled::D38s12;
104 /// // asin(0) == 0.
105 /// assert_eq!(D38s12::ZERO.asin(), D38s12::ZERO);
106 /// # }
107 /// ```
108 #[cfg(feature = "std")]
109 #[inline]
110 #[must_use]
111 pub fn asin_fast(self) -> Self {
112 Self::from_f64(self.to_f64().asin())
113 }
114
115 /// Arccosine of `self`. Returns radians in `[0, pi]`.
116 ///
117 /// `f64::acos` returns NaN for inputs outside `[-1, 1]`, which
118 /// [`Self::from_f64`] maps to `D38::ZERO`.
119 ///
120 /// # Precision
121 ///
122 /// Lossy: involves f64 at some point; result may lose precision.
123 ///
124 /// # Examples
125 ///
126 /// ```ignore
127 /// # #[cfg(feature = "std")]
128 /// # {
129 /// use decimal_scaled::{D38s12, DecimalConsts};
130 /// // acos(1) == 0.
131 /// assert_eq!(D38s12::ONE.acos(), D38s12::ZERO);
132 /// # }
133 /// ```
134 #[cfg(feature = "std")]
135 #[inline]
136 #[must_use]
137 pub fn acos_fast(self) -> Self {
138 Self::from_f64(self.to_f64().acos())
139 }
140
141 /// Arctangent of `self`. Returns radians in `(-pi/2, pi/2)`.
142 ///
143 /// Defined for the entire real line.
144 ///
145 /// # Precision
146 ///
147 /// Lossy: involves f64 at some point; result may lose precision.
148 ///
149 /// # Examples
150 ///
151 /// ```ignore
152 /// # #[cfg(feature = "std")]
153 /// # {
154 /// use decimal_scaled::D38s12;
155 /// // atan(0) == 0.
156 /// assert_eq!(D38s12::ZERO.atan(), D38s12::ZERO);
157 /// # }
158 /// ```
159 #[cfg(feature = "std")]
160 #[inline]
161 #[must_use]
162 pub fn atan_fast(self) -> Self {
163 Self::from_f64(self.to_f64().atan())
164 }
165
166 /// Four-quadrant arctangent of `self` (`y`) over `other` (`x`).
167 /// Returns radians in `(-pi, pi]`.
168 ///
169 /// Signature matches `f64::atan2(self, other)`: the receiver is
170 /// `y` and the argument is `x`.
171 ///
172 /// # Precision
173 ///
174 /// Lossy: involves f64 at some point; result may lose precision.
175 ///
176 /// # Examples
177 ///
178 /// ```ignore
179 /// # #[cfg(feature = "std")]
180 /// # {
181 /// use decimal_scaled::{D38s12, DecimalConsts};
182 /// // atan2(1, 1) ~= pi/4 (45 degrees, first quadrant).
183 /// let one = D38s12::ONE;
184 /// let result = one.atan2(one); // approximately D38s12::quarter_pi()
185 /// # }
186 /// ```
187 #[cfg(feature = "std")]
188 #[inline]
189 #[must_use]
190 pub fn atan2_fast(self, other: Self) -> Self {
191 Self::from_f64(self.to_f64().atan2(other.to_f64()))
192 }
193
194 // ── Hyperbolic ────────────────────────────────────────────────────
195
196 /// Hyperbolic sine of `self`.
197 ///
198 /// Defined for the entire real line. Saturates at large magnitudes
199 /// per [`Self::from_f64`].
200 ///
201 /// # Precision
202 ///
203 /// Lossy: involves f64 at some point; result may lose precision.
204 ///
205 /// # Examples
206 ///
207 /// ```ignore
208 /// # #[cfg(feature = "std")]
209 /// # {
210 /// use decimal_scaled::D38s12;
211 /// // sinh(0) == 0.
212 /// assert_eq!(D38s12::ZERO.sinh(), D38s12::ZERO);
213 /// # }
214 /// ```
215 #[cfg(feature = "std")]
216 #[inline]
217 #[must_use]
218 pub fn sinh_fast(self) -> Self {
219 Self::from_f64(self.to_f64().sinh())
220 }
221
222 /// Hyperbolic cosine of `self`.
223 ///
224 /// Defined for the entire real line; result is always >= 1.
225 /// Saturates at large magnitudes per [`Self::from_f64`].
226 ///
227 /// # Precision
228 ///
229 /// Lossy: involves f64 at some point; result may lose precision.
230 ///
231 /// # Examples
232 ///
233 /// ```ignore
234 /// # #[cfg(feature = "std")]
235 /// # {
236 /// use decimal_scaled::D38s12;
237 /// // cosh(0) == 1.
238 /// assert_eq!(D38s12::ZERO.cosh(), D38s12::ONE);
239 /// # }
240 /// ```
241 #[cfg(feature = "std")]
242 #[inline]
243 #[must_use]
244 pub fn cosh_fast(self) -> Self {
245 Self::from_f64(self.to_f64().cosh())
246 }
247
248 /// Hyperbolic tangent of `self`.
249 ///
250 /// Defined for the entire real line; range is `(-1, 1)`.
251 ///
252 /// # Precision
253 ///
254 /// Lossy: involves f64 at some point; result may lose precision.
255 ///
256 /// # Examples
257 ///
258 /// ```ignore
259 /// # #[cfg(feature = "std")]
260 /// # {
261 /// use decimal_scaled::D38s12;
262 /// // tanh(0) == 0.
263 /// assert_eq!(D38s12::ZERO.tanh(), D38s12::ZERO);
264 /// # }
265 /// ```
266 #[cfg(feature = "std")]
267 #[inline]
268 #[must_use]
269 pub fn tanh_fast(self) -> Self {
270 Self::from_f64(self.to_f64().tanh())
271 }
272
273 /// Inverse hyperbolic sine of `self`.
274 ///
275 /// Defined for the entire real line.
276 ///
277 /// # Precision
278 ///
279 /// Lossy: involves f64 at some point; result may lose precision.
280 ///
281 /// # Examples
282 ///
283 /// ```ignore
284 /// # #[cfg(feature = "std")]
285 /// # {
286 /// use decimal_scaled::D38s12;
287 /// // asinh(0) == 0.
288 /// assert_eq!(D38s12::ZERO.asinh(), D38s12::ZERO);
289 /// # }
290 /// ```
291 #[cfg(feature = "std")]
292 #[inline]
293 #[must_use]
294 pub fn asinh_fast(self) -> Self {
295 Self::from_f64(self.to_f64().asinh())
296 }
297
298 /// Inverse hyperbolic cosine of `self`.
299 ///
300 /// `f64::acosh` returns NaN for inputs less than 1, which
301 /// [`Self::from_f64`] maps to `D38::ZERO`.
302 ///
303 /// # Precision
304 ///
305 /// Lossy: involves f64 at some point; result may lose precision.
306 ///
307 /// # Examples
308 ///
309 /// ```ignore
310 /// # #[cfg(feature = "std")]
311 /// # {
312 /// use decimal_scaled::D38s12;
313 /// // acosh(1) == 0.
314 /// assert_eq!(D38s12::ONE.acosh(), D38s12::ZERO);
315 /// # }
316 /// ```
317 #[cfg(feature = "std")]
318 #[inline]
319 #[must_use]
320 pub fn acosh_fast(self) -> Self {
321 Self::from_f64(self.to_f64().acosh())
322 }
323
324 /// Inverse hyperbolic tangent of `self`.
325 ///
326 /// `f64::atanh` returns NaN for inputs outside `(-1, 1)`, which
327 /// [`Self::from_f64`] maps to `D38::ZERO`.
328 ///
329 /// # Precision
330 ///
331 /// Lossy: involves f64 at some point; result may lose precision.
332 ///
333 /// # Examples
334 ///
335 /// ```ignore
336 /// # #[cfg(feature = "std")]
337 /// # {
338 /// use decimal_scaled::D38s12;
339 /// // atanh(0) == 0.
340 /// assert_eq!(D38s12::ZERO.atanh(), D38s12::ZERO);
341 /// # }
342 /// ```
343 #[cfg(feature = "std")]
344 #[inline]
345 #[must_use]
346 pub fn atanh_fast(self) -> Self {
347 Self::from_f64(self.to_f64().atanh())
348 }
349
350 // ── Angle conversions ─────────────────────────────────────────────
351
352 /// Convert radians to degrees: `self * (180 / pi)`.
353 ///
354 /// Routed through `f64::to_degrees` so results match the de facto
355 /// reference produced by the rest of the Rust ecosystem. Multiplying
356 /// by a precomputed `D38` factor derived from `D38::pi()` would
357 /// diverge from f64 by a 1-LSB rescale rounding without any
358 /// practical determinism gain, since the f64 bridge is already the
359 /// precision floor.
360 ///
361 /// # Precision
362 ///
363 /// Lossy: involves f64 at some point; result may lose precision.
364 ///
365 /// # Examples
366 ///
367 /// ```ignore
368 /// # #[cfg(feature = "std")]
369 /// # {
370 /// use decimal_scaled::D38s12;
371 /// // to_degrees(0) == 0.
372 /// assert_eq!(D38s12::ZERO.to_degrees(), D38s12::ZERO);
373 /// # }
374 /// ```
375 #[cfg(feature = "std")]
376 #[inline]
377 #[must_use]
378 pub fn to_degrees_fast(self) -> Self {
379 Self::from_f64(self.to_f64().to_degrees())
380 }
381
382 /// Convert degrees to radians: `self * (pi / 180)`.
383 ///
384 /// Routed through `f64::to_radians`. See [`Self::to_degrees`] for
385 /// the rationale.
386 ///
387 /// # Precision
388 ///
389 /// Lossy: involves f64 at some point; result may lose precision.
390 ///
391 /// # Examples
392 ///
393 /// ```ignore
394 /// # #[cfg(feature = "std")]
395 /// # {
396 /// use decimal_scaled::D38s12;
397 /// // to_radians(0) == 0.
398 /// assert_eq!(D38s12::ZERO.to_radians(), D38s12::ZERO);
399 /// # }
400 /// ```
401 #[cfg(feature = "std")]
402 #[inline]
403 #[must_use]
404 pub fn to_radians_fast(self) -> Self {
405 Self::from_f64(self.to_f64().to_radians())
406 }
407}
408
409#[cfg(all(feature = "std", any(not(feature = "strict"), feature = "fast")))]
410impl<const SCALE: u32> D38<SCALE> {
411 /// Plain dispatcher: forwards to [`Self::sin_fast`] in this feature mode.
412 #[inline] #[must_use] pub fn sin(self) -> Self { self.sin_fast() }
413 /// Plain dispatcher: forwards to [`Self::cos_fast`] in this feature mode.
414 #[inline] #[must_use] pub fn cos(self) -> Self { self.cos_fast() }
415 /// Plain dispatcher: forwards to [`Self::tan_fast`] in this feature mode.
416 #[inline] #[must_use] pub fn tan(self) -> Self { self.tan_fast() }
417 /// Plain dispatcher: forwards to [`Self::asin_fast`] in this feature mode.
418 #[inline] #[must_use] pub fn asin(self) -> Self { self.asin_fast() }
419 /// Plain dispatcher: forwards to [`Self::acos_fast`] in this feature mode.
420 #[inline] #[must_use] pub fn acos(self) -> Self { self.acos_fast() }
421 /// Plain dispatcher: forwards to [`Self::atan_fast`] in this feature mode.
422 #[inline] #[must_use] pub fn atan(self) -> Self { self.atan_fast() }
423 /// Plain dispatcher: forwards to [`Self::atan2_fast`] in this feature mode.
424 #[inline] #[must_use] pub fn atan2(self, other: Self) -> Self { self.atan2_fast(other) }
425 /// Plain dispatcher: forwards to [`Self::sinh_fast`] in this feature mode.
426 #[inline] #[must_use] pub fn sinh(self) -> Self { self.sinh_fast() }
427 /// Plain dispatcher: forwards to [`Self::cosh_fast`] in this feature mode.
428 #[inline] #[must_use] pub fn cosh(self) -> Self { self.cosh_fast() }
429 /// Plain dispatcher: forwards to [`Self::tanh_fast`] in this feature mode.
430 #[inline] #[must_use] pub fn tanh(self) -> Self { self.tanh_fast() }
431 /// Plain dispatcher: forwards to [`Self::asinh_fast`] in this feature mode.
432 #[inline] #[must_use] pub fn asinh(self) -> Self { self.asinh_fast() }
433 /// Plain dispatcher: forwards to [`Self::acosh_fast`] in this feature mode.
434 #[inline] #[must_use] pub fn acosh(self) -> Self { self.acosh_fast() }
435 /// Plain dispatcher: forwards to [`Self::atanh_fast`] in this feature mode.
436 #[inline] #[must_use] pub fn atanh(self) -> Self { self.atanh_fast() }
437 /// Plain dispatcher: forwards to [`Self::to_degrees_fast`] in this feature mode.
438 #[inline] #[must_use] pub fn to_degrees(self) -> Self { self.to_degrees_fast() }
439 /// Plain dispatcher: forwards to [`Self::to_radians_fast`] in this feature mode.
440 #[inline] #[must_use] pub fn to_radians(self) -> Self { self.to_radians_fast() }
441}