1//! All the routines for calculating exp(x)
2//!
34use crate::*;
5use super::*;
678/// Calculate e^n
9pub(crate) fn impl_exp(n: BigDecimalRef, ctx: &Context) -> BigDecimal {
10use arithmetic::division::scaled_uint_division_into;
11use arithmetic::inverse::impl_inverse_uint_scale;
1213if n.is_zero() {
14return BigDecimal::one();
15 }
1617// n ~= x * 2^k
18let (x, k) = factor_two_to_k_scale(WithScale { value: n.digits, scale: n.scale });
1920let target_precision = ctx.precision().get();
2122// always at least 3 u64's worth of bits
23let target_precision_bits = digit_to_bit_count(target_precision).max(64 * 3) + kas u64;
2425let mut num = x.clone();
26let mut den = WithScale { value: 1u8.into(), scale: 0 };
2728// sum = 1 + x
29let mut sum = WithScale { value: 1u8.into(), scale: 0 };
30 addition::addassign_scaled_biguint(&mut sum, num.as_ref());
3132// we should have form `1.xxxxx` so only one digit is integer part
33if true {
{
match (&sum.count_int_digits(), &1) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(sum.count_int_digits(), 1);
3435let mut delta: WithScale<BigUint> = Default::default();
3637// assuming linear convergence, should break after N; we loop
38 // through 2*N for safety
39let stop = (target_precision * 2).max(10);
4041for i in 2..stop {
42// each loop iteration:
43 // num = x^i
44 // den = factorial(i)
45 // delta = num / den
46 // sum += delta
47num.mulassign_scaled_biguint(&x);
48 den.value *= i;
49 remove_trailing_zeros(&mut den, &[1]);
50 scaled_uint_division_into(&mut delta, &num, &den, target_precision_bits - i);
51 sum.addassign_scaled_biguint(&delta);
5253// we have converged if number of leading zeros in delta is
54 // larger than the target precision
55let leading_zero_count = delta.count_int_digits().neg();
56if leading_zero_count > target_precision as i64 {
57break;
58 }
59 }
6061// reuse 'delta' as scratchpad
62let mut tmp = delta.value;
6364// at this point: sum = exp(n / 2^k)
65 //
66 // we now square it 'k' times to get final result
67arithmetic::pow::pow_2_k_scaled_biguint(
68&mut sum, &mut tmp, k, target_precision_bits69 );
7071let result = BigDecimal::from(sum);
72if n.sign == Sign::Minus {
73return ctx.invert(&result);
74 } else {
75return ctx.round_decimal(result);
76 }
77}
7879/// Factor scaled biguint by 2^k
80///
81/// Returns pair of sacled-BigUint in range [0.0, 0.5] and 'k', the
82/// number of times to square the BigUint to return the number to
83/// the original value.
84///
85fn factor_two_to_k_scale(n: WithScale<&BigUint>) -> (WithScale<BigUint>, u16) {
86let log2_n = n.value.bits() as f64;
87let log2_s = (n.scale as f64) * LOG2_10;
88let k = 1.0 + log2_n - log2_s;
89if k <= 0.5 {
90let r = n.value.clone();
91return ((r, n.scale).into(), 0);
92 }
9394let k = k.ceil() as u64;
95let mut x = BigUint::from(5u8).pow(k);
96x*= n.value;
9798let mut result = WithScale {
99 value: x,
100 scale: n.scale + kas i64,
101 };
102103// strip trailing zeros at different speeds (last one must be '1')
104remove_trailing_zeros(&mut result, &[8, 1]);
105106 (result, k.as_())
107}
108109/// Remove trailing zeros by divmoding n by given powers of ten
110///
111/// Multiple powers may be chosen to speed up removal (should end with '1'
112/// to remove all zero, i.e. mod-10)
113///
114fn remove_trailing_zeros<'a>(
115 n: &mut WithScale<BigUint>,
116 powers: impl IntoIterator<Item=&'a u8>,
117) {
118for &i in powers.into_iter() {
119if true {
if !(i < 20) { ::core::panicking::panic("assertion failed: i < 20") };
};debug_assert!(i < 20);
120121let s = 10u64.pow(i as u32);
122while (&n.value % s).is_zero() {
123 n.value /= s;
124 n.scale -= i as i64;
125 }
126 }
127}
128129#[cfg(test)]
130mod test {
131use super::*;
132133include!("exp.tests.rs");
134}