devela/num/float/wrapper/
shared_series.rs

1// devela::num::float::wrapper::shared_series
2//
3//! Shared methods implemented using Taylor Series.
4//
5// TOC
6// - impl_float_shared_series!
7// - CONST tables
8// - fn helpers
9
10#[allow(unused_imports)]
11use super::super::shared_docs::*;
12use crate::{cfor, iif, paste, Float};
13
14/// Implements methods independently of any features
15///
16/// $f:   the floating-point type.
17/// $uf:  unsigned int type with the same bit-size.
18/// $ue:  unsigned int type used for integer exponentiation and number of terms (u32).
19/// $cap: the capability feature that enables the given implementation. E.g "_float_f32".
20/// $cmp: the feature that enables some methods depending on Compare. E.g "_cmp_f32".
21macro_rules! impl_float_shared_series {
22    () => {
23        impl_float_shared_series![
24            (f32:u32, u32):"_float_f32":"_cmp_f32",
25            (f64:u64, u32):"_float_f64":"_cmp_f64"
26        ];
27    };
28
29    ($( ($f:ty:$uf:ty, $ue:ty) : $cap:literal : $cmp:literal ),+) => {
30        $( impl_float_shared_series![@$f:$uf, $ue, $cap:$cmp]; )+
31    };
32    (@$f:ty:$uf:ty, $ue:ty, $cap:literal : $cmp:literal) => {
33        #[doc = crate::doc_availability!(feature = $cap)]
34        ///
35        /// # *Common methods with or without `std` or `libm`*.
36        ///   *Implemented using Taylor series.*
37        #[cfg(feature = $cap )]
38        // #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
39        impl Float<$f> {
40            /// Raises itself to the `y` floating point power using the Taylor series via the
41            /// `exp` and `ln` functions.
42            ///
43            /// # Formulation
44            #[doc = FORMULA_POWF_SERIES!()]
45            ///
46            /// See also [`ln_series_terms`][Self::ln_series_terms].
47            ///
48            /// The terms for the exponential function are calculated using
49            /// [`exp_series_terms`][Self::exp_series_terms] using $y\cdot\ln(x)$.
50            pub const fn powf_series(self, y: $f, ln_x_terms: $ue) -> Float<$f> {
51                let xabs = self.abs().0;
52                if xabs == 0.0 {
53                    iif![Float(y).abs().0 == 0.0; Self::ONE; Self::ZERO]
54                } else {
55                    let ln_x = Float(xabs).ln_series(ln_x_terms).0;
56                    let power = Float(y * ln_x);
57                    let exp_y_terms = power.exp_series_terms();
58                    let result = power.exp_series(exp_y_terms);
59                    iif![self.is_sign_negative(); Float(-result.0); result]
60                }
61            }
62
63            /// Computes the exponential function $e^x$ using Taylor series expansion.
64            ///
65            /// # Formulation
66            #[doc = FORMULA_EXP_SERIES!()]
67            ///
68            /// See also [`exp_series_terms`][Self::exp_series_terms].
69            pub const fn exp_series(self, terms: $ue) -> Float<$f> {
70                iif![self.0 < 0.0; return Float(1.0 / Float(-self.0).exp_series(terms).0)];
71                let (mut result, mut term) = (1.0, 1.0);
72                let mut i = 1;
73                while i <= terms {
74                    term *= self.0 / i as $f;
75                    result += term;
76                    i += 1;
77                }
78                Float(result)
79            }
80
81            /// Determines the number of terms needed for [`exp_series`][Self::exp_series]
82            /// to reach a stable result based on the input value.
83            #[doc = TABLE_EXP_SERIES_TERMS!()]
84            pub const fn exp_series_terms(self) -> $ue { paste! {
85                Self::[<exp_series_terms_ $f>](self.0)
86            }}
87
88            /// Calculates $ e^x - 1 $ using the Taylor series expansion.
89            ///
90            /// # Formulation
91            #[doc = FORMULA_EXP_M1_SERIES!()]
92            ///
93            /// See also [`exp_series_terms`][Self::exp_series_terms].
94            pub const fn exp_m1_series(self, terms: $ue) -> Float<$f> {
95                if self.0 < 0.0 {
96                    Float(1.0 / Float(-self.0).exp_m1_series(terms).0)
97                } else if self.0 > 0.001 {
98                    Float(self.exp_series(terms).0 - 1.0)
99                } else {
100                    let (mut result, mut term, mut factorial) = (0.0, self.0, 1.0);
101                    let mut i = 1;
102                    while i <= terms {
103                        result += term;
104                        factorial *= (i + 1) as $f;
105                        term *= self.0 / factorial;
106                        i += 1;
107                    }
108                    Float(result)
109                }
110            }
111
112            /// Calculates $ 2^x $ using the Taylor series expansion.
113            ///
114            /// # Formulation
115            #[doc = FORMULA_EXP2_SERIES!()]
116            ///
117            /// The maximum values with a representable result are:
118            /// 127 for `f32` and 1023 for `f64`.
119            pub const fn exp2_series(self, terms: $ue) -> Float<$f> {
120                let (mut result, mut term) = (1.0, self.0 * Self::LN_2.0);
121                let mut n = 1;
122                while n < terms {
123                    result += term;
124                    term *= self.0 * Self::LN_2.0 / (n as $f + 1.0);
125                    n += 1;
126                }
127                Float(result)
128            }
129
130            /// Determines the number of terms needed for [`exp2_series`][Self::exp2_series]
131            /// to reach a stable result based on the input value.
132            #[doc = TABLE_EXP2_SERIES_TERMS!()]
133            pub const fn exp2_series_terms(self) -> $ue { paste! {
134                Self::[<exp2_series_terms_ $f>](self.0)
135            }}
136
137            /// Computes the natural logarithm of `self` using a Taylor-Mercator series expansion.
138            ///
139            /// This method is more efficient for values of `self` near 1. Values too
140            /// small or too big could be impractical to calculate with precision.
141            ///
142            /// # Formulation
143            #[doc = FORMULA_LN_SERIES!()]
144            ///
145            /// See also [`ln_series_terms`][Self::ln_series_terms].
146            pub const fn ln_series(self, terms: $ue) -> Float<$f> {
147                if self.0 < 0.0 {
148                    Self::NAN
149                } else if self.0 > 0.0 {
150                    let mut sum = 0.0;
151                    let y = (self.0 - 1.0) / (self.0 + 1.0);
152                    let mut y_pow = y;
153                    cfor![i in 0..terms => {
154                        sum += y_pow / (2 * i + 1) as $f;
155                        y_pow *= y * y;
156                    }];
157                    Float(2.0 * sum)
158                } else {
159                    Self::NEG_INFINITY
160                }
161            }
162
163            /// Computes the natural logarithm of `1 + self`
164            /// using a Taylor-Mercator series expansion.
165            ///
166            /// This method is more efficient for values of `self` near 0.
167            /// Values too small or too big could be impractical to calculate with precision.
168            ///
169            /// Returns `ln(1+self)` more accurately
170            /// than if the operations were performed separately.
171            ///
172            /// See also [`ln_series_terms`][Self::ln_series_terms].
173            pub const fn ln_1p_series(self, terms: $ue) -> Float<$f> {
174                if self.0 < -1.0 {
175                    Self::NAN
176                } else if self.0 > -1.0 {
177                    let x1 = self.0 + 1.0;
178                    let mut sum = 0.0;
179                    let y = (x1 - 1.0) / (x1 + 1.0);
180                    let mut y_pow = y;
181                    cfor![i in 0..terms => {
182                        sum += y_pow / (2 * i + 1) as $f;
183                        y_pow *= y * y;
184                    }];
185                    Float(2.0 * sum)
186                } else {
187                    Self::NEG_INFINITY
188                }
189            }
190
191            /// Computes the logarithm to the given `base` using the change of base formula.
192            ///
193            /// # Formulation
194            #[doc = FORMULA_LOG_SERIES!()]
195            ///
196            /// See also [`ln_series_terms`][Self::ln_series_terms].
197            pub const fn log_series(self, base: $f, terms: $ue) -> Float<$f> {
198                if base <= 0.0 {
199                    Self::NAN
200                // The logarithm with a base of 1 is undefined except when the argument is also 1.
201                } else if Float(base - 1.0).abs().0 < Self::MEDIUM_MARGIN.0 { // + robust
202                // } else if base == 1.0 { // good enough for direct input
203                    #[expect(clippy::float_cmp, reason = "we've already checked it with a margin")]
204                    { iif![self.0 == 1.0; Self::NAN; Self::NEG_INFINITY] }
205                } else {
206                    Float(self.ln_series(terms).0 / base).ln_series(terms)
207                }
208            }
209
210            /// Computes the base-2 logarithm using the change of base formula.
211            ///
212            /// # Formulation
213            #[doc = FORMULA_LOG2_SERIES!()]
214            ///
215            /// See also [`ln_series_terms`][Self::ln_series_terms].
216            pub const fn log2_series(self, terms: $ue) -> Float<$f> {
217                Float(self.ln_series(terms).0 / 2.0).ln_series(terms)
218            }
219
220            /// Computes the base-10 logarithm using the change of base formula.
221            ///
222            /// # Formulation
223            #[doc = FORMULA_LOG10_SERIES!()]
224            ///
225            /// See also [`ln_series_terms`][Self::ln_series_terms].
226            pub const fn log10_series(self, terms: $ue) -> Float<$f> {
227                Float(self.ln_series(terms).0 / 10.0).ln_series(terms)
228            }
229
230            /// Determines the number of terms needed for [`exp2_series`][Self::exp2_series]
231            /// to reach a stable result based on the input value.
232            #[doc = TABLE_LN_SERIES_TERMS!()]
233            #[must_use]
234            pub const fn ln_series_terms(self) -> $ue { paste! {
235                Self::[<ln_series_terms_ $f>](self.0)
236            }}
237
238            /// The sine calculated using Taylor series expansion.
239            ///
240            /// # Formulation
241            #[doc = FORMULA_SIN_SERIES!()]
242            ///
243            /// This Taylor series converges relatively quickly and uniformly
244            /// over the entire domain.
245            #[doc = TABLE_SIN_SERIES_TERMS!()]
246            pub const fn sin_series(self, terms: $ue) -> Float<$f> {
247                let x = self.clamp_nan(-Self::PI.0, Self::PI.0).0;
248                let (mut sin, mut term, mut factorial) = (x, x, 1.0);
249                let mut i = 1;
250                while i < terms {
251                    term *= -x * x;
252                    factorial *= ((2 * i + 1) * (2 * i)) as $f;
253                    sin += term / factorial;
254                    i += 1;
255                }
256                Float(sin)
257            }
258
259            /// Computes the cosine using taylor series expansion.
260            ///
261            /// # Formulation
262            #[doc = FORMULA_COS_SERIES!()]
263            ///
264            /// This Taylor series converges relatively quickly and uniformly
265            /// over the entire domain.
266            #[doc = TABLE_COS_SERIES_TERMS!()]
267            pub const fn cos_series(self, terms: $ue) -> Float<$f> {
268                let x = self.clamp_nan(-Self::PI.0, Self::PI.0).0;
269                let (mut cos, mut term, mut factorial) = (1.0, 1.0, 1.0);
270                let mut i = 1;
271                while i < terms {
272                    term *= -x * x;
273                    factorial *= ((2 * i - 1) * (2 * i)) as $f;
274                    cos += term / factorial;
275                    i += 1;
276                }
277                Float(cos)
278            }
279
280            /// Computes the sine and the cosine using Taylor series expansion.
281            pub const fn sin_cos_series(self, terms: $ue) -> (Float<$f>, Float<$f>) {
282                (self.sin_series(terms), self.cos_series(terms))
283            }
284
285            /// Computes the tangent using Taylor series expansion of sine and cosine.
286            ///
287            /// # Formulation
288            #[doc = FORMULA_TAN_SERIES!()]
289            ///
290            /// The tangent function has singularities and is not defined for
291            /// `cos(x) = 0`. This function clamps `self` within an appropriate range
292            /// to avoid such issues.
293            ///
294            /// The Taylor series for sine and cosine converge relatively quickly
295            /// and uniformly over the entire domain.
296            #[doc = TABLE_TAN_SERIES_TERMS!()]
297            pub const fn tan_series(self, terms: $ue) -> Float<$f> {
298                let x = self.clamp_nan(-Self::PI.0 / 2.0 + 0.0001, Self::PI.0 / 2.0 - 0.0001);
299                let (sin, cos) = x.sin_cos_series(terms);
300                iif![cos.abs().0 < 0.0001; return Self::MAX];
301                Float(sin.0 / cos.0)
302            }
303
304            /// Computes the arcsine using Taylor series expansion.
305            ///
306            /// # Formulation
307            #[doc = FORMULA_ASIN_SERIES!()]
308            ///
309            /// asin is undefined for $ |x| > 1 $ and in that case returns `NaN`.
310            ///
311            /// The series converges more slowly near the edges of the domain
312            /// (i.e., as `self` approaches -1 or 1). For more accurate results,
313            /// especially near these boundary values, a higher number of terms
314            /// may be necessary.
315            ///
316            /// See also [`asin_series_terms`][Self::asin_series_terms].
317            pub const fn asin_series(self, terms: $ue) -> Float<$f> {
318                iif![self.abs().0 > 1.0; return Self::NAN];
319                let (mut asin_approx, mut multiplier, mut power_x) = (0.0, 1.0, self.0);
320                let mut i = 0;
321                while i < terms {
322                    if i != 0 {
323                        multiplier *= (2 * i - 1) as $f / (2 * i) as $f;
324                        power_x *= self.0 * self.0;
325                    }
326                    asin_approx += multiplier * power_x / (2 * i + 1) as $f;
327                    i += 1;
328                }
329                Float(asin_approx)
330            }
331
332            /// Determines the number of terms needed for [`asin_series`][Self::asin_series]
333            /// to reach a stable result based on the input value.
334            ///
335            #[doc = TABLE_ASIN_SERIES_TERMS!()]
336            #[must_use]
337            pub const fn asin_series_terms(self) -> $ue { paste! {
338                Self::[<asin_acos_series_terms_ $f>](self.0)
339            }}
340
341            /// Computes the arccosine using the Taylor expansion of arcsine.
342            ///
343            /// # Formulation
344            #[doc = FORMULA_ACOS_SERIES!()]
345            ///
346            /// See the [`asin_series_terms`][Self#method.asin_series_terms] table for
347            /// information about the number of `terms` needed.
348            pub const fn acos_series(self, terms: $ue) -> Float<$f> {
349                iif![self.abs().0 > 1.0; return Self::NAN];
350                Float(Self::FRAC_PI_2.0 - self.asin_series(terms).0)
351            }
352
353            /// Determines the number of terms needed for [`acos_series`][Self::acos_series]
354            /// to reach a stable result based on the input value.
355            ///
356            /// The table is the same as [`asin_series_terms`][Self::asin_series_terms].
357            #[must_use]
358            pub const fn acos_series_terms(self) -> $ue { paste! {
359                Self::[<asin_acos_series_terms_ $f>](self.0)
360            }}
361
362            /// Computes the arctangent using Taylor series expansion.
363            ///
364            /// # Formulation
365            #[doc = FORMULA_ATAN_SERIES!()]
366            ///
367            /// The series converges more slowly near the edges of the domain
368            /// (i.e., as `self` approaches -1 or 1). For more accurate results,
369            /// especially near these boundary values, a higher number of terms
370            /// may be necessary.
371            ///
372            /// See also [`atan_series_terms`][Self::atan_series_terms].
373            pub const fn atan_series(self, terms: $ue) -> Float<$f> {
374                if self.abs().0 > 1.0 {
375                    if self.0 > 0.0 {
376                        Float(Self::FRAC_PI_2.0 - Float(1.0 / self.0).atan_series(terms).0)
377                    } else {
378                        Float(-Self::FRAC_PI_2.0 - Float(1.0 / self.0).atan_series(terms).0)
379                    }
380                } else {
381                    let (mut atan_approx, mut num, mut sign) = (Self::ZERO.0, self.0, Self::ONE.0);
382                    let mut i = 0;
383                    while i < terms {
384                        if i > 0 {
385                            num *= self.0 * self.0;
386                            sign = -sign;
387                        }
388                        atan_approx += sign * num / (2.0 * i as $f + 1.0);
389                        i += 1;
390                    }
391                    Float(atan_approx)
392                }
393            }
394
395            /// Determines the number of terms needed for [`atan_series`][Self::atan_series]
396            /// to reach a stable result based on the input value.
397            #[doc = TABLE_ATAN_SERIES_TERMS!()]
398            #[must_use]
399            pub const fn atan_series_terms(self) -> $ue { paste! {
400                Self::[<atan_series_terms_ $f>](self.0)
401            }}
402
403            /// Computes the four quadrant arctangent of `self` and `other`
404            /// using Taylor series expansion.
405            ///
406            /// See also [`atan_series_terms`][Self::atan_series_terms].
407            pub const fn atan2_series(self, other: $f, terms: $ue) -> Float<$f> {
408                if other > 0.0 {
409                    Float(self.0 / other).atan_series(terms)
410                } else if self.0 >= 0.0 && other < 0.0 {
411                    Float(Float(self.0 / other).atan_series(terms).0 + Self::PI.0)
412                } else if self.0 < 0.0 && other < 0.0 {
413                    Float(Float(self.0 / other).atan_series(terms).0 - Self::PI.0)
414                } else if self.0 > 0.0 && other == 0.0 {
415                    Float(Self::PI.0 / 2.0)
416                } else if self.0 < 0.0 && other == 0.0 {
417                    Float(-Self::PI.0 / 2.0)
418                } else {
419                    // self and other are both zero, undefined behavior
420                    Self::NAN
421                }
422            }
423
424            /// The hyperbolic sine calculated using Taylor series expansion
425            /// via the exponent formula.
426            ///
427            /// # Formulation
428            #[doc = FORMULA_SINH_SERIES!()]
429            ///
430            /// See the [`exp_series_terms`][Self#method.exp_series_terms] table for
431            /// information about the number of `terms` needed.
432            pub const fn sinh_series(self, terms: $ue) -> Float<$f> {
433                Float((self.exp_series(terms).0 - -self.exp_series(terms).0) / 2.0)
434            }
435
436            /// The hyperbolic cosine calculated using Taylor series expansion
437            /// via the exponent formula.
438            ///
439            /// # Formulation
440            #[doc = FORMULA_COSH_SERIES!()]
441            ///
442            /// See the [`exp_series_terms`][Self#method.exp_series_terms] table for
443            /// information about the number of `terms` needed.
444            pub const fn cosh_series(self, terms: $ue) -> Float<$f> {
445                Float((self.exp_series(terms).0 + -self.exp_series(terms).0) / 2.0)
446            }
447
448            /// Computes the hyperbolic tangent using Taylor series expansion of
449            /// hyperbolic sine and cosine.
450            ///
451            /// # Formulation
452            #[doc = FORMULA_TANH_SERIES!()]
453            ///
454            /// See the [`exp_series_terms`][Self#method.exp_series_terms] table for
455            /// information about the number of `terms` needed.
456            pub const fn tanh_series(self, terms: $ue) -> Float<$f> {
457                let sinh_approx = self.sinh_series(terms);
458                let cosh_approx = self.cosh_series(terms);
459                Float(sinh_approx.0 / cosh_approx.0)
460            }
461
462            /// Computes the inverse hyperbolic sine using the natural logarithm definition.
463            ///
464            /// # Formulation
465            #[doc = FORMULA_ASINH_SERIES!()]
466            ///
467            /// See also [`ln_series_terms`][Self::ln_series_terms].
468            pub const fn asinh_series(self, terms: $ue) -> Float<$f> {
469                let sqrt = Float(self.0 * self.0 + 1.0).sqrt_nr().0;
470                Float(self.0 + sqrt).ln_series(terms)
471            }
472
473            /// Computes the inverse hyperbolic cosine using the natural logarithm definition.
474            ///
475            /// # Formulation
476            #[doc = FORMULA_ACOSH_SERIES!()]
477            ///
478            /// See also [`ln_series_terms`][Self::ln_series_terms].
479            pub const fn acosh_series(self, terms: $ue) -> Float<$f> {
480                if self.0 < 1.0 {
481                    Self::NAN
482                } else {
483                    let sqrt = Float(self.0 * self.0 - 1.0).sqrt_nr().0;
484                    Float(self.0 + sqrt).ln_series(terms)
485                }
486            }
487
488            /// Computes the inverse hyperbolic tangent using the natural logarithm definition.
489            ///
490            /// # Formulation
491            #[doc = FORMULA_ATANH_SERIES!()]
492            ///
493            /// See also [`ln_series_terms`][Self::ln_series_terms].
494            pub const fn atanh_series(self, terms: $ue) -> Float<$f> {
495                if self.0 >= 1.0 {
496                    Self::INFINITY
497                } else if self.0 <= -1.0 {
498                    Self::NEG_INFINITY
499                } else {
500                    Float(Float((self.0 + 1.0) / (1.0 - self.0)).ln_series(terms).0 * 0.5)
501                }
502            }
503        }
504    };
505}
506impl_float_shared_series!();
507
508crate::CONST! { pub(crate),
509TABLE_EXP_SERIES_TERMS = "
510The following table shows the required number of `terms` needed
511to reach the most precise result for both `f32` and `f64`:
512```txt
513  value     t_f32  t_f64
514-------------------------
515± 0.001 →       3     5
516± 0.100 →       6     10
517± 1.000 →      11     18
518± 10.000 →     32     46
519± 20.000 →     49     68
520± 50.000 →     92    119
521± 88.722 →    143    177  (max for f32 == f32::MAX.ln())
522± 150.000 →   ---    261
523± 300.000 →   ---    453
524± 500.000 →   ---    692
525± 709.782 →   ---    938  (max for f64 == f64:MAX.ln())
526```";
527TABLE_EXP2_SERIES_TERMS = "
528The following table shows the required number of `terms` needed
529to reach the most precise result for both `f32` and `f64`:
530```txt
531  value     t_f32  t_f64
532-------------------------
533± 0.3 →        8     13
534± 3.0 →       15     25
535± 7.0 →       22     34
536± 15.0 →      34     49
537± 31.0 →      52     71
538± 63.0 →      84    110
539± 127.999 →  144    178 (max for f32)
540± 255.0 →    ---    298
541± 511.0 →    ---    520
542± 1023.999 → ---    939 (max for f64)
543```";
544TABLE_LN_SERIES_TERMS = "
545The following table shows the required number of `terms` needed
546to reach the most precise result for both `f32` and `f64`:
547```txt
548  value      t_f32  t_f64
549--------------------------
550± 0.00001 →  81181 536609
551± 0.0001 →   12578  59174
552± 0.001 →     1923   6639
553± 0.01. →      245    720
554± 0.1 →         32     80
555± 0.5 →          8     17
556± 1. →           1      1
557± 2. →           8     17
558± 10. →         32     80
559± 100. →       245    720
560± 1000. →     1923   6639
561± 10000. →   12578  59174
562± 100000. →  81181 536609
563/// ```
564```";
565TABLE_SIN_SERIES_TERMS = "
566The following table shows the required number of `terms` needed
567to reach the most precise result for both `f32` and `f64`:
568```txt
569  value     t_f32  t_f64
570-------------------------
571± 0.001 →      3      4
572± 0.100 →      4      6
573± 0.300 →      5      7
574± 0.500 →      5      8
575± 0.700 →      6      9
576± 0.900 →      6     10
577± 0.999 →      6     10
578```";
579TABLE_COS_SERIES_TERMS = "
580The following table shows the required number of `terms` needed
581to reach the most precise result for both `f32` and `f64`:
582```txt
583  value     t_f32  t_f64
584-------------------------
585± 0.001 →      3      4
586± 0.100 →      4      6
587± 0.300 →      5      8
588± 0.500 →      6      9
589± 0.700 →      6     10
590± 0.900 →      7     10
591± 0.999 →      7     11
592```";
593TABLE_TAN_SERIES_TERMS = "
594The following table shows the required number of `terms` needed
595to reach the most precise result for both `f32` and `f64`:
596```txt
597  value     t_f32  t_f64
598-------------------------
599± 0.001 →      3      4
600± 0.100 →      4      6
601± 0.300 →      5      8
602± 0.500 →      6      9
603± 0.700 →      6     10
604± 0.900 →      7     10
605± 0.999 →      7     11
606```";
607TABLE_ASIN_SERIES_TERMS = "
608The following table shows the required number of `terms` needed
609to reach the most precise result for both `f32` and `f64`:
610```txt
611  value     t_f32  t_f64
612-------------------------
613± 0.001 →      3      4
614± 0.100 →      5      9
615± 0.300 →      7     15
616± 0.500 →     10     24
617± 0.700 →     18     44
618± 0.900 →     47    134
619± 0.990 →    333   1235
620± 0.999 →   1989  10768
621```";
622TABLE_ATAN_SERIES_TERMS = "
623The following table shows the required number of `terms` needed
624to reach the most precise result for both `f32` and `f64`:
625```txt
626  value     t_f32  t_f64
627-------------------------
628± 0.001 →      3      4
629± 0.100 →      5      9
630± 0.300 →      7     15
631± 0.500 →     12     26
632± 0.700 →     20     47
633± 0.900 →     61    152
634± 0.990 →    518   1466
635± 0.999 →   4151  13604
636```";
637}
638
639#[rustfmt::skip]
640#[cfg(feature = "_float_f32")]
641impl Float<f32> {
642    #[must_use]
643    pub(super) const fn asin_acos_series_terms_f32(x: f32) -> u32 {
644        let abs_a = Float(x).abs().0;
645        if abs_a <= 0.1 { 5
646        } else if abs_a <= 0.3 { 7
647        } else if abs_a <= 0.5 { 10
648        } else if abs_a <= 0.7 { 18
649        } else if abs_a <= 0.9 { 47
650        } else if abs_a <= 0.99 { 333
651        } else { 1989 // computed for 0.999
652        }
653    }
654    #[must_use]
655    pub(super) const fn atan_series_terms_f32(x: f32) -> u32 {
656        let abs_a = Float(x).abs().0;
657        if abs_a <= 0.1 { 5
658        } else if abs_a <= 0.3 { 7
659        } else if abs_a <= 0.5 { 12
660        } else if abs_a <= 0.7 { 20
661        } else if abs_a <= 0.9 { 61
662        } else if abs_a <= 0.99 { 518
663        } else { 4151 // computed for 0.999
664        }
665    }
666    #[must_use]
667    pub(super) const fn exp_series_terms_f32(x: f32) -> u32 {
668        let abs_a = Float(x).abs().0;
669        if abs_a <= 0.001 { 3
670        } else if abs_a <= 0.1 { 6
671        } else if abs_a <= 1.0 { 11
672        } else if abs_a <= 10.0 { 32
673        } else if abs_a <= 20.0 { 49
674        } else if abs_a <= 50.0 { 92
675        } else { 143 // computed for max computable value f32::MAX.ln()
676        }
677    }
678    #[must_use]
679    pub(super) const fn exp2_series_terms_f32(x: f32) -> u32 {
680        let abs_a = Float(x).abs().0;
681        if abs_a <= 0.3 { 8
682        } else if abs_a <= 3.0 { 15
683        } else if abs_a <= 7.0 { 22
684        } else if abs_a <= 15.0 { 34
685        } else if abs_a <= 31.0 { 52
686        } else if abs_a <= 63.0 { 84
687        } else { 144 // computed for max computable value f64::MAX.ln()
688        }
689    }
690    #[must_use]
691    pub(super) const fn ln_series_terms_f32(x: f32) -> u32 {
692        let x = Float(x).abs().0;
693        let x = if x == 0.0 { return 0;
694        } else if x <= 1. { 1. / x } else { x };
695
696        if x <= 10. { 32
697        } else if x <= 100. { 245
698        } else if x <= 1_000. { 1_923
699        } else if x <= 10_000. { 12_578
700        } else if x <= 100_000. { 81_181
701        } else if x <= 1_000_000. { 405_464
702        } else if x <= 10_000_000. { 2_027_320 // from now one prev * 5 …
703        } else if x <= 100_000_000. { 10_136_600
704        } else if x <= 1_000_000_000. { 50_683_000
705        } else { 253_415_000 }
706        // 32 * 7 = 224
707        // 245 * 7 = 1715
708        // 1923 * 7 = 13461
709        // 12578 * 7 = 88046
710        // 81181 * 5 = 405905
711    }
712}
713
714#[rustfmt::skip]
715#[cfg(feature = "_float_f64")]
716impl Float<f64> {
717    #[must_use]
718    pub(super) const fn asin_acos_series_terms_f64(x: f64) -> u32 {
719        let abs_a = Float(x).abs().0;
720        if abs_a <= 0.1 { 9
721        } else if abs_a <= 0.3 { 15
722        } else if abs_a <= 0.5 { 24
723        } else if abs_a <= 0.7 { 44
724        } else if abs_a <= 0.9 { 134
725        } else if abs_a <= 0.99 { 1235
726        } else { 10768 // computed for 0.999
727        }
728    }
729    #[must_use]
730    pub(super) const fn atan_series_terms_f64(x: f64) -> u32 {
731        let abs_a = Float(x).abs().0;
732        if abs_a <= 0.1 { 9
733        } else if abs_a <= 0.3 { 15
734        } else if abs_a <= 0.5 { 26
735        } else if abs_a <= 0.7 { 47
736        } else if abs_a <= 0.9 { 152
737        } else if abs_a <= 0.99 { 1466
738        } else { 13604 // computed for 0.999
739        }
740    }
741    #[must_use]
742    pub(super) const fn exp_series_terms_f64(x: f64) -> u32 {
743        let abs_a = Float(x).abs().0;
744        if abs_a <= 0.001 { 5
745        } else if abs_a <= 0.1 { 10
746        } else if abs_a <= 1.0 { 18
747        } else if abs_a <= 10.0 { 46
748        } else if abs_a <= 20.0 { 68
749        } else if abs_a <= 50.0 { 119
750        } else if abs_a <= 89.0 { 177
751        } else if abs_a <= 150.0 { 261
752        } else if abs_a <= 300.0 { 453
753        } else if abs_a <= 500.0 { 692
754        } else { 938 // computed for max computable value 709.782
755        }
756    }
757    #[must_use]
758    pub(super) const fn exp2_series_terms_f64(x: f64) -> u32 {
759        let abs_a = Float(x).abs().0;
760        if abs_a <= 0.3 { 13
761        } else if abs_a <= 3.0 { 25
762        } else if abs_a <= 7.0 { 34
763        } else if abs_a <= 15.0 { 49
764        } else if abs_a <= 31.0 { 71
765        } else if abs_a <= 63.0 { 110
766        } else if abs_a <= 128.0 { 178
767        } else if abs_a <= 255.0 { 298
768        } else if abs_a <= 511.0 { 520
769        } else { 939 // computed for max computable value 1023.999
770        }
771    }
772    #[must_use]
773    pub(super) const fn ln_series_terms_f64(x: f64) -> u32 {
774        let x = Float(x).abs().0;
775        let x = if x == 0.0 { return 0;
776        } else if x <= 1. { 1. / x } else { x };
777
778        if x <= 10. { 80
779        } else if x <= 100. { 720
780        } else if x <= 1_000. { 6_639
781        } else if x <= 10_000. { 59_174
782        } else if x <= 100_000. { 536_609
783        } else if x <= 1_000_000. { 4_817_404
784        } else if x <= 10_000_000. { 43_356_636 // from now on prev * 9
785        } else if x <= 100_000_000. { 390_209_724
786        } else { 3_511_887_516 }
787        // 80 * 9 = 720
788        // 720 * 9 = 6480
789        // 6639 * 9 = 59751
790        // 59174 * 9 = 532566
791    }
792}