Skip to main content

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}