fpmath/generic/
exp10.rs

1use super::exp::exp_inner_common;
2use super::{round_as_i_f, Exp};
3use crate::traits::{Int as _, Like};
4
5pub(crate) trait Exp10<L = Like<Self>>: Exp {
6    fn log2_10() -> Self;
7    fn log10_2_hi() -> Self;
8    fn log10_2_lo() -> Self;
9    fn ln_10() -> Self;
10    fn ln_10_hi() -> Self;
11    fn ln_10_lo() -> Self;
12    fn exp10_lo_th() -> Self;
13    fn exp10_hi_th() -> Self;
14}
15
16/// Returns 10 raised to `x`.
17pub(crate) fn exp10<F: Exp10>(x: F) -> F {
18    if x >= F::exp10_hi_th() {
19        // also handles x = inf
20        F::INFINITY
21    } else if x <= F::exp10_lo_th() {
22        // also handles x = -inf
23        F::ZERO
24    } else {
25        let e = x.raw_exp();
26        if e == F::RawExp::ZERO {
27            // x is zero or subnormal
28            // 10^x ~= 1
29            F::one()
30        } else if e == F::MAX_RAW_EXP {
31            // x is NaN, propagate
32            x
33        } else {
34            exp10_inner(x)
35        }
36    }
37}
38
39fn exp10_inner<F: Exp10>(x: F) -> F {
40    // Split x into k, r_hi, r_lo such as:
41    //  - x = k*log10(2) + (r_hi + r_lo)*log10(e)
42    //  - k is an integer
43    //  - |r| <= 0.5*ln(2)
44    let (k, r_hi, r_lo) = exp10_split(x);
45
46    // Calculate 10^x = exp(k*ln(2) + r_hi + r_lo)
47    exp_inner_common(k, r_hi, r_lo)
48}
49
50/// Splits `x` into `(k, r_hi, r_lo)`
51///
52/// Such as:
53/// * `x = k*log10(2) + (r_hi + r_lo)*log10(e)`
54/// * `k` is an integer
55/// * `|r| <= 0.5*ln(2)`
56#[inline]
57fn exp10_split<F: Exp10>(x: F) -> (i32, F, F) {
58    let y = x * F::log2_10();
59    let (k, kf) = round_as_i_f(y);
60    // `kf * LOG10_2_HI` is exact because the lower bits
61    // of `LOG10_2_HI` are zero.
62    let t_hi = x - kf * F::log10_2_hi();
63    let t_lo = -kf * F::log10_2_lo();
64    let (t_hi, t_lo) = F::norm_hi_lo_splitted(t_hi, t_lo);
65
66    let r_hi = t_hi * F::ln_10_hi();
67    let r_lo = t_hi * F::ln_10_lo() + t_lo * F::ln_10();
68
69    (k, r_hi, r_lo)
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::traits::Float;
75    use crate::FloatMath;
76
77    fn test<F: Float + FloatMath>(lo_th: &str, hi_th: &str) {
78        use crate::exp10;
79
80        let f = F::parse;
81
82        let lo_th = f(lo_th);
83        let hi_th = f(hi_th);
84
85        assert_is_nan!(exp10(F::NAN));
86        assert_total_eq!(exp10(F::INFINITY), F::INFINITY);
87        assert_total_eq!(exp10(F::neg_infinity()), F::ZERO);
88        assert_total_eq!(exp10(F::ZERO), F::one());
89        assert_total_eq!(exp10(-F::ZERO), F::one());
90        assert_total_eq!(exp10(F::one()), f("10"));
91        assert_total_eq!(exp10(F::two()), f("100"));
92        assert_total_eq!(exp10(lo_th), F::ZERO);
93        assert_total_eq!(exp10(lo_th - F::one()), F::ZERO);
94        assert_total_eq!(exp10(lo_th - F::two()), F::ZERO);
95        assert_total_eq!(exp10(hi_th), F::INFINITY);
96        assert_total_eq!(exp10(hi_th + F::one()), F::INFINITY);
97        assert_total_eq!(exp10(hi_th + F::two()), F::INFINITY);
98    }
99
100    #[test]
101    fn test_f32() {
102        test::<f32>("-45.9", "38.9");
103    }
104
105    #[cfg(feature = "soft-float")]
106    #[test]
107    fn test_soft_f32() {
108        test::<crate::SoftF32>("-45.9", "38.9");
109    }
110
111    #[test]
112    fn test_f64() {
113        test::<f64>("-323.9", "308.9");
114    }
115
116    #[cfg(feature = "soft-float")]
117    #[test]
118    fn test_soft_f64() {
119        test::<crate::SoftF64>("-323.9", "308.9");
120    }
121}