1use crate::{dd::Double, float::{Native, horner, sealed::Core}};
3
4const EXP_64: [f64; 10] = [
6 2.749197269771299e-07, 2.7639186521124947e-06, 2.4801900316905035e-05, 0.00019841173089486715, 0.0013888888529025865, 0.008333333382940285,
7 0.04166666666820299, 0.16666666666564642, 0.4999999999999798, 1.0000000000000056,
8];
9const EXP_32: [f64; 5] = [0.008247390389442444, 0.04189976677298546, 0.16668395698070526, 0.4999912679195404, 0.9999994039535522];
10const EXP_NARROW: [f64; 6] = [0.0013941108433972674, 0.008375126398153335, 0.04166635289677515, 0.16666415514653277, 0.5000000047117757, 1.000000037716214];
12const H_64: [f64; 11] = [
14 2.5052099500253996e-08, 2.762626280199822e-07, 2.7557414780064832e-06, 2.4801504602137864e-05, 0.00019841269707477827, 0.001388888893225886,
15 0.008333333333397829, 0.041666666666573905, 0.16666666666666563, 0.5000000000000006, 1.0,
16];
17const H_32: [f64; 6] = [0.0013888871762901545, 0.00836906861513853, 0.041667137295007706, 0.16666506230831146, 0.4999999701976776, 1.0];
18
19#[inline]
21pub(crate) fn clamp<R: Native>(x: R, l: R) -> R { if x < -l { -l } else if x > l { l } else { x } }
22
23#[inline]
25fn reduce<R: Native, const FUSED: bool>(x: R) -> (R, R) { reduce_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x) }
26
27#[inline]
29fn exp_parts<R: Native, const FUSED: bool>(x: R, lo: R, k: R) -> R { parts_with(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x, lo, k) }
30
31#[inline]
32pub(crate) fn exp<R: Native, const FUSED: bool>(x: R) -> R { exp_parts::<R, FUSED>(x, R::neg_zero(), R::neg_zero()) }
33
34#[derive(Clone, Copy, Debug)]
36pub struct Exp;
37
38#[inline]
40pub(crate) fn exp_times<R: Native, const FUSED: bool>(x: R, k: R) -> R { exp_parts::<R, FUSED>(x, R::neg_zero(), k) }
41
42#[inline]
43pub(crate) fn expm1<R: Native, const FUSED: bool>(x: R) -> R { expm1_times::<R, FUSED>(x, R::zero()) }
44
45#[derive(Clone, Copy, Debug)]
47pub struct Expm1;
48
49#[inline]
51pub(crate) fn expm1_times<R: Native, const FUSED: bool>(x: R, k: R) -> R { minus_one_formula(&crate::recipe::NativeOps::<FUSED>::DEFAULT, x, k) }
52
53#[inline]
55pub(crate) fn exp_plus<R: Native, const FUSED: bool>(x: Double<R>) -> R { let x = Double::sum(x.hi, x.lo); exp_parts::<R, FUSED>(x.hi, x.lo, R::neg_zero()) }
56
57#[inline]
59pub(crate) fn exp_narrow<const FUSED: bool>(x: f64) -> f64 {
60 let (n, r) = reduce::<f64, FUSED>(clamp(x, 110.0));
61 r.mla::<FUSED>(horner::<f64, FUSED, 6>(r, EXP_NARROW), 1.0) * f64::pow2(n + f64::SHIFT)
62}
63
64#[inline(always)]
65fn reduce_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> (O::V, O::V) {
66 let shift = crate::recipe::rounding_shift(o);
67 let n = o.subtract(o.multiply_add(x, o.number(F::narrow(std::f64::consts::LOG2_E)), shift), shift);
68 let (hi, lo) = crate::recipe::ln2(o);
69 (n, o.multiply_add(n, o.negate(lo), o.multiply_add(n, o.negate(hi), x)))
70}
71
72#[inline(always)]
73fn reduced_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, r: O::V) -> O::V {
74 let h = crate::recipe::horner(o, r, if o.width() == crate::buffer::FloatWidth::F64 { &EXP_64 } else { &EXP_32 });
75 o.multiply_add(r, h, o.number(F::one()))
76}
77
78#[inline(always)]
79fn clamp_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
80 let limit = o.number(F::narrow(if o.width() == crate::buffer::FloatWidth::F64 { 1100.0 } else { 150.0 }));
81 let low = o.negate(limit);
82 o.select(o.less(x, low), low, o.select(o.less(limit, x), limit, x))
83}
84
85#[inline(always)]
86fn parts_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, lo: O::V, k: O::V) -> O::V {
87 let c = clamp_with(o, x);
88 let (n, r) = reduce_with(o, c);
89 let r = o.select(o.equal(c, x), o.add(r, lo), r);
90 crate::recipe::ldexp(o, reduced_with(o, r), o.add(n, k))
91}
92
93#[inline(always)]
94pub(crate) fn formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V) -> O::V {
95 let zero = o.number(-F::zero());
96 parts_with(o, x, zero, zero)
97}
98
99#[inline(always)]
100fn minus_one_with<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, r: O::V, scale: O::V) -> O::V {
101 let h = crate::recipe::horner(o, r, if o.width() == crate::buffer::FloatWidth::F64 { &H_64 } else { &H_32 });
102 o.multiply_add(o.multiply(r, h), scale, o.subtract(scale, o.number(F::one())))
103}
104
105#[inline]
106pub(crate) fn scaled_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, scale: O::V) -> O::V {
107 parts_with(o, x, o.number(-F::zero()), scale)
108}
109
110#[inline]
111pub(crate) fn minus_one_formula<F: crate::Float, O: crate::recipe::Ops<F> + ?Sized>(o: &O, x: O::V, scale: O::V) -> O::V {
112 let (low, top) = if o.width() == crate::buffer::FloatWidth::F64 { (-40.0, 1023.0) } else { (-18.0, 127.0) };
113 let low = o.number(F::narrow(low));
114 let (n, r) = reduce_with(o, clamp_with(o, o.select(o.less(x, low), low, x)));
115 let top = o.number(F::narrow(top));
116 let m = o.select(o.less(top, n), top, n);
117 let shift = crate::recipe::rounding_shift(o);
118 let first = minus_one_with(o, r, crate::recipe::pow2(o, o.add(m, shift)));
119 o.multiply(first, crate::recipe::pow2(o, o.add(o.add(o.subtract(n, m), scale), shift)))
120}