/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rust_decimal-1.41.0/src/maths.rs
Line | Count | Source |
1 | | use crate::prelude::*; |
2 | | use num_traits::pow::Pow; |
3 | | |
4 | | // Approximation of 1/ln(10) = 0.4342944819032518276511289189 |
5 | | const LN10_INVERSE: Decimal = Decimal::from_parts_raw(1763037029, 1670682625, 235431510, 1835008); |
6 | | // PI / 8 |
7 | | const EIGHTH_PI: Decimal = Decimal::from_parts_raw(2822163429, 3244459792, 212882598, 1835008); |
8 | | |
9 | | // Table representing {index}! — used in tests to verify factorial values. |
10 | | #[cfg(test)] |
11 | | const FACTORIAL: [Decimal; 28] = [ |
12 | | Decimal::from_parts(1, 0, 0, false, 0), |
13 | | Decimal::from_parts(1, 0, 0, false, 0), |
14 | | Decimal::from_parts(2, 0, 0, false, 0), |
15 | | Decimal::from_parts(6, 0, 0, false, 0), |
16 | | Decimal::from_parts(24, 0, 0, false, 0), |
17 | | // 5! |
18 | | Decimal::from_parts(120, 0, 0, false, 0), |
19 | | Decimal::from_parts(720, 0, 0, false, 0), |
20 | | Decimal::from_parts(5040, 0, 0, false, 0), |
21 | | Decimal::from_parts(40320, 0, 0, false, 0), |
22 | | Decimal::from_parts(362880, 0, 0, false, 0), |
23 | | // 10! |
24 | | Decimal::from_parts(3628800, 0, 0, false, 0), |
25 | | Decimal::from_parts(39916800, 0, 0, false, 0), |
26 | | Decimal::from_parts(479001600, 0, 0, false, 0), |
27 | | Decimal::from_parts(1932053504, 1, 0, false, 0), |
28 | | Decimal::from_parts(1278945280, 20, 0, false, 0), |
29 | | // 15! |
30 | | Decimal::from_parts(2004310016, 304, 0, false, 0), |
31 | | Decimal::from_parts(2004189184, 4871, 0, false, 0), |
32 | | Decimal::from_parts(4006445056, 82814, 0, false, 0), |
33 | | Decimal::from_parts(3396534272, 1490668, 0, false, 0), |
34 | | Decimal::from_parts(109641728, 28322707, 0, false, 0), |
35 | | // 20! |
36 | | Decimal::from_parts(2192834560, 566454140, 0, false, 0), |
37 | | Decimal::from_parts(3099852800, 3305602358, 2, false, 0), |
38 | | Decimal::from_parts(3772252160, 4003775155, 60, false, 0), |
39 | | Decimal::from_parts(862453760, 1892515369, 1401, false, 0), |
40 | | Decimal::from_parts(3519021056, 2470695900, 33634, false, 0), |
41 | | // 25! |
42 | | Decimal::from_parts(2076180480, 1637855376, 840864, false, 0), |
43 | | Decimal::from_parts(2441084928, 3929534124, 21862473, false, 0), |
44 | | Decimal::from_parts(1484783616, 3018206259, 590286795, false, 0), |
45 | | ]; |
46 | | |
47 | | /// Trait exposing various mathematical operations that can be applied using a Decimal. This is only |
48 | | /// present when the `maths` feature has been enabled, e.g. by adding the crate with |
49 | | // `cargo add rust_decimal --features maths` and importing in your Rust file with `use rust_decimal::MathematicalOps;` |
50 | | pub trait MathematicalOps { |
51 | | /// The estimated exponential function, e<sup>x</sup>. Stops calculating when it is within |
52 | | /// tolerance of roughly `0.0000002`. |
53 | | fn exp(&self) -> Decimal; |
54 | | |
55 | | /// The estimated exponential function, e<sup>x</sup>. Stops calculating when it is within |
56 | | /// tolerance of roughly `0.0000002`. Returns `None` on overflow. |
57 | | fn checked_exp(&self) -> Option<Decimal>; |
58 | | |
59 | | /// The estimated exponential function, e<sup>x</sup> using the `tolerance` provided as a hint |
60 | | /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating |
61 | | /// sooner at the potential cost of a slightly less accurate result. |
62 | | fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal; |
63 | | |
64 | | /// The estimated exponential function, e<sup>x</sup> using the `tolerance` provided as a hint |
65 | | /// as to when to stop calculating. A larger tolerance will cause the number to stop calculating |
66 | | /// sooner at the potential cost of a slightly less accurate result. |
67 | | /// Returns `None` on overflow. |
68 | | fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option<Decimal>; |
69 | | |
70 | | /// Raise self to the given integer exponent: x<sup>y</sup> |
71 | | fn powi(&self, exp: i64) -> Decimal; |
72 | | |
73 | | /// Raise self to the given integer exponent x<sup>y</sup> returning `None` on overflow. |
74 | | fn checked_powi(&self, exp: i64) -> Option<Decimal>; |
75 | | |
76 | | /// Raise self to the given unsigned integer exponent: x<sup>y</sup> |
77 | | fn powu(&self, exp: u64) -> Decimal; |
78 | | |
79 | | /// Raise self to the given unsigned integer exponent x<sup>y</sup> returning `None` on overflow. |
80 | | fn checked_powu(&self, exp: u64) -> Option<Decimal>; |
81 | | |
82 | | /// Raise self to the given floating point exponent: x<sup>y</sup> |
83 | | fn powf(&self, exp: f64) -> Decimal; |
84 | | |
85 | | /// Raise self to the given floating point exponent x<sup>y</sup> returning `None` on overflow. |
86 | | fn checked_powf(&self, exp: f64) -> Option<Decimal>; |
87 | | |
88 | | /// Raise self to the given Decimal exponent: x<sup>y</sup>. If `exp` is not whole then the approximation |
89 | | /// e<sup>y*ln(x)</sup> is used. |
90 | | fn powd(&self, exp: Decimal) -> Decimal; |
91 | | |
92 | | /// Raise self to the given Decimal exponent x<sup>y</sup> returning `None` on overflow. |
93 | | /// If `exp` is not whole then the approximation e<sup>y*ln(x)</sup> is used. |
94 | | fn checked_powd(&self, exp: Decimal) -> Option<Decimal>; |
95 | | |
96 | | /// The square root of a Decimal. Uses a standard Babylonian method. |
97 | | fn sqrt(&self) -> Option<Decimal>; |
98 | | |
99 | | /// Calculates the natural logarithm for a Decimal calculated using Taylor's series. |
100 | | fn ln(&self) -> Decimal; |
101 | | |
102 | | /// Calculates the checked natural logarithm for a Decimal calculated using Taylor's series. |
103 | | /// Returns `None` for negative numbers or zero. |
104 | | fn checked_ln(&self) -> Option<Decimal>; |
105 | | |
106 | | /// Calculates the base 10 logarithm of a specified Decimal number. |
107 | | fn log10(&self) -> Decimal; |
108 | | |
109 | | /// Calculates the checked base 10 logarithm of a specified Decimal number. |
110 | | /// Returns `None` for negative numbers or zero. |
111 | | fn checked_log10(&self) -> Option<Decimal>; |
112 | | |
113 | | /// Abramowitz Approximation of Error Function from [wikipedia](https://en.wikipedia.org/wiki/Error_function#Numerical_approximations) |
114 | | fn erf(&self) -> Decimal; |
115 | | |
116 | | /// The Cumulative distribution function for a Normal distribution |
117 | | fn norm_cdf(&self) -> Decimal; |
118 | | |
119 | | /// The Probability density function for a Normal distribution. |
120 | | fn norm_pdf(&self) -> Decimal; |
121 | | |
122 | | /// The Probability density function for a Normal distribution returning `None` on overflow. |
123 | | fn checked_norm_pdf(&self) -> Option<Decimal>; |
124 | | |
125 | | /// Computes the sine of a number (in radians). |
126 | | /// Panics upon overflow. |
127 | | fn sin(&self) -> Decimal; |
128 | | |
129 | | /// Computes the checked sine of a number (in radians). |
130 | | fn checked_sin(&self) -> Option<Decimal>; |
131 | | |
132 | | /// Computes the cosine of a number (in radians). |
133 | | /// Panics upon overflow. |
134 | | fn cos(&self) -> Decimal; |
135 | | |
136 | | /// Computes the checked cosine of a number (in radians). |
137 | | fn checked_cos(&self) -> Option<Decimal>; |
138 | | |
139 | | /// Computes the tangent of a number (in radians). |
140 | | /// Panics upon overflow or upon approaching a limit. |
141 | | fn tan(&self) -> Decimal; |
142 | | |
143 | | /// Computes the checked tangent of a number (in radians). |
144 | | /// Returns None on limit. |
145 | | fn checked_tan(&self) -> Option<Decimal>; |
146 | | } |
147 | | |
148 | | impl MathematicalOps for Decimal { |
149 | 0 | fn exp(&self) -> Decimal { |
150 | 0 | match self.checked_exp() { |
151 | 0 | Some(d) => d, |
152 | | None => { |
153 | 0 | if self.is_sign_negative() { |
154 | 0 | panic!("Exp underflowed") |
155 | | } else { |
156 | 0 | panic!("Exp overflowed") |
157 | | } |
158 | | } |
159 | | } |
160 | 0 | } |
161 | | |
162 | 0 | fn checked_exp(&self) -> Option<Decimal> { |
163 | 0 | crate::ops::wide::exp_wide(self) |
164 | 0 | } |
165 | | |
166 | 0 | fn exp_with_tolerance(&self, tolerance: Decimal) -> Decimal { |
167 | 0 | match self.checked_exp_with_tolerance(tolerance) { |
168 | 0 | Some(d) => d, |
169 | | None => { |
170 | 0 | if self.is_sign_negative() { |
171 | 0 | panic!("Exp underflowed") |
172 | | } else { |
173 | 0 | panic!("Exp overflowed") |
174 | | } |
175 | | } |
176 | | } |
177 | 0 | } |
178 | | |
179 | 0 | fn checked_exp_with_tolerance(&self, tolerance: Decimal) -> Option<Decimal> { |
180 | 0 | if self.is_zero() { |
181 | 0 | return Some(Decimal::ONE); |
182 | 0 | } |
183 | 0 | if self.is_sign_negative() { |
184 | 0 | let mut flipped = *self; |
185 | 0 | flipped.set_sign_positive(true); |
186 | 0 | let exp = flipped.checked_exp_with_tolerance(tolerance)?; |
187 | 0 | return Decimal::ONE.checked_div(exp); |
188 | 0 | } |
189 | | |
190 | | // exp(x) = sum_i x^i / i!, let q_i := x^i/i! |
191 | | // Avoid computing x^i directly as it will quickly outgrow exp(x) for x > 1. |
192 | | // Instead we compute q_i = x*(x/2)*(x/3)*...*(x/i) |
193 | | |
194 | 0 | let mut result = self.checked_add(Decimal::ONE)?; |
195 | 0 | let mut term = *self; |
196 | | |
197 | 0 | for i in 2..200u32 { |
198 | 0 | let i_dec = Decimal::from_u32(i).unwrap(); |
199 | 0 | term = self.checked_mul(term.checked_div(i_dec)?)?; |
200 | 0 | result = result.checked_add(term)?; |
201 | 0 | if term <= tolerance { |
202 | 0 | break; |
203 | 0 | } |
204 | | } |
205 | | |
206 | 0 | Some(result) |
207 | 0 | } |
208 | | |
209 | 0 | fn powi(&self, exp: i64) -> Decimal { |
210 | 0 | match self.checked_powi(exp) { |
211 | 0 | Some(result) => result, |
212 | 0 | None => panic!("Pow overflowed"), |
213 | | } |
214 | 0 | } |
215 | | |
216 | 0 | fn checked_powi(&self, exp: i64) -> Option<Decimal> { |
217 | | // For negative exponents we change x^-y into 1 / x^y. |
218 | | // Otherwise, we calculate a standard unsigned exponent |
219 | 0 | if exp >= 0 { |
220 | 0 | return self.checked_powu(exp as u64); |
221 | 0 | } |
222 | | |
223 | | // Get the unsigned exponent |
224 | 0 | let exp = exp.unsigned_abs(); |
225 | 0 | let pow = self.checked_powu(exp)?; |
226 | 0 | Decimal::ONE.checked_div(pow) |
227 | 0 | } |
228 | | |
229 | 0 | fn powu(&self, exp: u64) -> Decimal { |
230 | 0 | match self.checked_powu(exp) { |
231 | 0 | Some(result) => result, |
232 | 0 | None => panic!("Pow overflowed"), |
233 | | } |
234 | 0 | } |
235 | | |
236 | 0 | fn checked_powu(&self, exp: u64) -> Option<Decimal> { |
237 | 0 | crate::ops::wide::powu_wide(self, exp) |
238 | 0 | } |
239 | | |
240 | 0 | fn powf(&self, exp: f64) -> Decimal { |
241 | 0 | match self.checked_powf(exp) { |
242 | 0 | Some(result) => result, |
243 | 0 | None => panic!("Pow overflowed"), |
244 | | } |
245 | 0 | } |
246 | | |
247 | 0 | fn checked_powf(&self, exp: f64) -> Option<Decimal> { |
248 | 0 | let exp = Decimal::from_f64(exp)?; |
249 | 0 | self.checked_powd(exp) |
250 | 0 | } |
251 | | |
252 | 0 | fn powd(&self, exp: Decimal) -> Decimal { |
253 | 0 | match self.checked_powd(exp) { |
254 | 0 | Some(result) => result, |
255 | 0 | None => panic!("Pow overflowed"), |
256 | | } |
257 | 0 | } |
258 | | |
259 | 0 | fn checked_powd(&self, exp: Decimal) -> Option<Decimal> { |
260 | 0 | if exp.is_zero() { |
261 | 0 | return Some(Decimal::ONE); |
262 | 0 | } |
263 | 0 | if self.is_zero() { |
264 | 0 | return Some(Decimal::ZERO); |
265 | 0 | } |
266 | 0 | if self.is_one() { |
267 | 0 | return Some(Decimal::ONE); |
268 | 0 | } |
269 | 0 | if exp.is_one() { |
270 | 0 | return Some(*self); |
271 | 0 | } |
272 | | |
273 | | // If the scale is 0 then it's a trivial calculation |
274 | 0 | let exp = exp.normalize(); |
275 | 0 | if exp.scale() == 0 { |
276 | 0 | if exp.mid() != 0 || exp.hi() != 0 { |
277 | | // Exponent way too big |
278 | 0 | return None; |
279 | 0 | } |
280 | | |
281 | 0 | return if exp.is_sign_negative() { |
282 | 0 | self.checked_powi(-(exp.lo() as i64)) |
283 | | } else { |
284 | 0 | self.checked_powu(exp.lo() as u64) |
285 | | }; |
286 | 0 | } |
287 | | |
288 | | // We do some approximations since we've got a decimal exponent. |
289 | | // For positive bases: a^b = exp(b*ln(a)) |
290 | 0 | let negative = self.is_sign_negative(); |
291 | 0 | let e = self.abs().ln().checked_mul(exp)?; |
292 | 0 | let mut result = e.checked_exp()?; |
293 | 0 | result.set_sign_negative(negative); |
294 | 0 | Some(result) |
295 | 0 | } |
296 | | |
297 | 0 | fn sqrt(&self) -> Option<Decimal> { |
298 | 0 | if self.is_sign_negative() { |
299 | 0 | return None; |
300 | 0 | } |
301 | | |
302 | 0 | if self.is_zero() { |
303 | 0 | return Some(Decimal::ZERO); |
304 | 0 | } |
305 | | |
306 | | // Start with an arbitrary number as the first guess |
307 | 0 | let mut result = self / Decimal::TWO; |
308 | | // Too small to represent, so we start with self |
309 | | // Future iterations could actually avoid using a decimal altogether and use a buffered |
310 | | // vector, only combining back into a decimal on return |
311 | 0 | if result.is_zero() { |
312 | 0 | result = *self; |
313 | 0 | } |
314 | 0 | let mut last = result + Decimal::ONE; |
315 | | |
316 | | // Keep going while the difference is larger than the tolerance |
317 | 0 | let mut circuit_breaker = 0; |
318 | 0 | while last != result { |
319 | 0 | circuit_breaker += 1; |
320 | 0 | assert!(circuit_breaker < 1000, "geo mean circuit breaker"); |
321 | | |
322 | 0 | last = result; |
323 | 0 | result = (result + self / result) / Decimal::TWO; |
324 | | } |
325 | | |
326 | 0 | Some(result) |
327 | 0 | } |
328 | | |
329 | | #[cfg(feature = "maths-nopanic")] |
330 | | fn ln(&self) -> Decimal { |
331 | | match self.checked_ln() { |
332 | | Some(result) => result, |
333 | | None => Decimal::ZERO, |
334 | | } |
335 | | } |
336 | | |
337 | | #[cfg(not(feature = "maths-nopanic"))] |
338 | 0 | fn ln(&self) -> Decimal { |
339 | 0 | match self.checked_ln() { |
340 | 0 | Some(result) => result, |
341 | | None => { |
342 | 0 | if self.is_sign_negative() { |
343 | 0 | panic!("Unable to calculate ln for negative numbers") |
344 | 0 | } else if self.is_zero() { |
345 | 0 | panic!("Unable to calculate ln for zero") |
346 | | } else { |
347 | 0 | panic!("Calculation of ln failed for unknown reasons") |
348 | | } |
349 | | } |
350 | | } |
351 | 0 | } |
352 | | |
353 | 0 | fn checked_ln(&self) -> Option<Decimal> { |
354 | 0 | crate::ops::wide::ln_wide(self) |
355 | 0 | } |
356 | | |
357 | | #[cfg(feature = "maths-nopanic")] |
358 | | fn log10(&self) -> Decimal { |
359 | | match self.checked_log10() { |
360 | | Some(result) => result, |
361 | | None => Decimal::ZERO, |
362 | | } |
363 | | } |
364 | | |
365 | | #[cfg(not(feature = "maths-nopanic"))] |
366 | 0 | fn log10(&self) -> Decimal { |
367 | 0 | match self.checked_log10() { |
368 | 0 | Some(result) => result, |
369 | | None => { |
370 | 0 | if self.is_sign_negative() { |
371 | 0 | panic!("Unable to calculate log10 for negative numbers") |
372 | 0 | } else if self.is_zero() { |
373 | 0 | panic!("Unable to calculate log10 for zero") |
374 | | } else { |
375 | 0 | panic!("Calculation of log10 failed for unknown reasons") |
376 | | } |
377 | | } |
378 | | } |
379 | 0 | } |
380 | | |
381 | 0 | fn checked_log10(&self) -> Option<Decimal> { |
382 | | use crate::ops::array::{div_by_u32, is_all_zero}; |
383 | | // Early exits |
384 | 0 | if self.is_sign_negative() || self.is_zero() { |
385 | 0 | return None; |
386 | 0 | } |
387 | 0 | if self.is_one() { |
388 | 0 | return Some(Decimal::ZERO); |
389 | 0 | } |
390 | | |
391 | | // This uses a very basic method for calculating log10. We know the following is true: |
392 | | // log10(n) = ln(n) / ln(10) |
393 | | // From this we can perform some small optimizations: |
394 | | // 1. ln(10) is a constant |
395 | | // 2. Multiplication is faster than division, so we can pre-calculate the constant 1/ln(10) |
396 | | // This allows us to then simplify log10(n) to: |
397 | | // log10(n) = C * ln(n) |
398 | | |
399 | | // Before doing all of this however, we see if there are simple calculations to be made. |
400 | 0 | let scale = self.scale(); |
401 | 0 | let mut working = self.mantissa_array3(); |
402 | | |
403 | | // Check for scales less than 1 as an early exit |
404 | 0 | if scale > 0 && working[2] == 0 && working[1] == 0 && working[0] == 1 { |
405 | 0 | return Some(Decimal::from_parts(scale, 0, 0, true, 0)); |
406 | 0 | } |
407 | | |
408 | | // Loop for detecting bordering base 10 values |
409 | 0 | let mut result = 0; |
410 | 0 | let mut base10 = true; |
411 | 0 | while !is_all_zero(&working) { |
412 | 0 | let remainder = div_by_u32(&mut working, 10u32); |
413 | 0 | if remainder != 0 { |
414 | 0 | base10 = false; |
415 | 0 | break; |
416 | 0 | } |
417 | 0 | result += 1; |
418 | 0 | if working[2] == 0 && working[1] == 0 && working[0] == 1 { |
419 | 0 | break; |
420 | 0 | } |
421 | | } |
422 | 0 | if base10 { |
423 | 0 | return Some((result - scale as i32).into()); |
424 | 0 | } |
425 | | |
426 | 0 | self.checked_ln().map(|result| LN10_INVERSE * result) |
427 | 0 | } |
428 | | |
429 | 0 | fn erf(&self) -> Decimal { |
430 | 0 | if self.is_sign_positive() { |
431 | 0 | let one = &Decimal::ONE; |
432 | | |
433 | 0 | let xa1 = self * Decimal::from_parts(705230784, 0, 0, false, 10); |
434 | 0 | let xa2 = self.powi(2) * Decimal::from_parts(422820123, 0, 0, false, 10); |
435 | 0 | let xa3 = self.powi(3) * Decimal::from_parts(92705272, 0, 0, false, 10); |
436 | 0 | let xa4 = self.powi(4) * Decimal::from_parts(1520143, 0, 0, false, 10); |
437 | 0 | let xa5 = self.powi(5) * Decimal::from_parts(2765672, 0, 0, false, 10); |
438 | 0 | let xa6 = self.powi(6) * Decimal::from_parts(430638, 0, 0, false, 10); |
439 | | |
440 | 0 | let sum = one + xa1 + xa2 + xa3 + xa4 + xa5 + xa6; |
441 | 0 | one - (one / sum.powi(16)) |
442 | | } else { |
443 | 0 | -self.abs().erf() |
444 | | } |
445 | 0 | } |
446 | | |
447 | 0 | fn norm_cdf(&self) -> Decimal { |
448 | 0 | (Decimal::ONE + (self / Decimal::from_parts(2318911239, 3292722, 0, false, 16)).erf()) / Decimal::TWO |
449 | 0 | } |
450 | | |
451 | 0 | fn norm_pdf(&self) -> Decimal { |
452 | 0 | match self.checked_norm_pdf() { |
453 | 0 | Some(d) => d, |
454 | 0 | None => panic!("Norm Pdf overflowed"), |
455 | | } |
456 | 0 | } |
457 | | |
458 | 0 | fn checked_norm_pdf(&self) -> Option<Decimal> { |
459 | 0 | let sqrt2pi = Decimal::from_parts_raw(2133383024, 2079885984, 1358845910, 1835008); |
460 | 0 | let factor = -self.checked_powi(2)?; |
461 | 0 | let factor = factor.checked_div(Decimal::TWO)?; |
462 | 0 | factor.checked_exp()?.checked_div(sqrt2pi) |
463 | 0 | } |
464 | | |
465 | 0 | fn sin(&self) -> Decimal { |
466 | 0 | match self.checked_sin() { |
467 | 0 | Some(x) => x, |
468 | 0 | None => panic!("Sin overflowed"), |
469 | | } |
470 | 0 | } |
471 | | |
472 | 0 | fn checked_sin(&self) -> Option<Decimal> { |
473 | 0 | crate::ops::wide::sin_wide(self) |
474 | 0 | } |
475 | | |
476 | 0 | fn cos(&self) -> Decimal { |
477 | 0 | match self.checked_cos() { |
478 | 0 | Some(x) => x, |
479 | 0 | None => panic!("Cos overflowed"), |
480 | | } |
481 | 0 | } |
482 | | |
483 | 0 | fn checked_cos(&self) -> Option<Decimal> { |
484 | 0 | crate::ops::wide::cos_wide(self) |
485 | 0 | } |
486 | | |
487 | 0 | fn tan(&self) -> Decimal { |
488 | 0 | match self.checked_tan() { |
489 | 0 | Some(x) => x, |
490 | 0 | None => panic!("Tan overflowed"), |
491 | | } |
492 | 0 | } |
493 | | |
494 | 0 | fn checked_tan(&self) -> Option<Decimal> { |
495 | 0 | if self.is_zero() { |
496 | 0 | return Some(Decimal::ZERO); |
497 | 0 | } |
498 | 0 | if self.is_sign_negative() { |
499 | | // -Tan(-x) |
500 | 0 | return (-self).checked_tan().map(|x| -x); |
501 | 0 | } |
502 | 0 | if self >= &Decimal::TWO_PI { |
503 | | // Reduce large numbers early - we can do this using rem to constrain to a range |
504 | 0 | let adjusted = self.checked_rem(Decimal::TWO_PI)?; |
505 | 0 | return adjusted.checked_tan(); |
506 | 0 | } |
507 | | // Reduce to 0 <= x <= PI |
508 | 0 | if self >= &Decimal::PI { |
509 | | // Tan(x-π) |
510 | 0 | return (self - Decimal::PI).checked_tan(); |
511 | 0 | } |
512 | | // Reduce to 0 <= x <= PI/2 |
513 | 0 | if self > &Decimal::HALF_PI { |
514 | | // We can use the symmetrical function inside the first quadrant |
515 | | // e.g. tan(x) = -tan((PI/2 - x) + PI/2) |
516 | 0 | return ((Decimal::HALF_PI - self) + Decimal::HALF_PI).checked_tan().map(|x| -x); |
517 | 0 | } |
518 | | |
519 | | // It has now been reduced to 0 <= x <= PI/2. If it is >= PI/4 we can make it even smaller |
520 | | // by calculating tan(PI/2 - x) and taking the reciprocal |
521 | 0 | if self > &Decimal::QUARTER_PI { |
522 | 0 | return match (Decimal::HALF_PI - self).checked_tan() { |
523 | 0 | Some(x) => Decimal::ONE.checked_div(x), |
524 | 0 | None => None, |
525 | | }; |
526 | 0 | } |
527 | | |
528 | | // Due the way that tan(x) sharply tends towards infinity, we try to optimize |
529 | | // the resulting accuracy by using Trigonometric identity when > PI/8. We do this by |
530 | | // replacing the angle with one that is half as big. |
531 | 0 | if self > &EIGHTH_PI { |
532 | | // Work out tan(x/2) |
533 | 0 | let tan_half = (self / Decimal::TWO).checked_tan()?; |
534 | | // Work out the dividend i.e. 2tan(x/2) |
535 | 0 | let dividend = Decimal::TWO.checked_mul(tan_half)?; |
536 | | |
537 | | // Work out the divisor i.e. 1 - tan^2(x/2) |
538 | 0 | let squared = tan_half.checked_mul(tan_half)?; |
539 | 0 | let divisor = Decimal::ONE - squared; |
540 | | // Treat this as infinity |
541 | 0 | if divisor.is_zero() { |
542 | 0 | return None; |
543 | 0 | } |
544 | 0 | return dividend.checked_div(divisor); |
545 | 0 | } |
546 | | |
547 | | // Do a polynomial approximation based upon the Maclaurin series. |
548 | | // This can be simplified to something like: |
549 | | // |
550 | | // ∑(n=1,3,5,7,9)(f(n)(0)/n!)x^n |
551 | | // |
552 | | // First few expansions (which we leverage): |
553 | | // (f'(0)/1!)x^1 + (f'''(0)/3!)x^3 + (f'''''(0)/5!)x^5 + (f'''''''/7!)x^7 |
554 | | // |
555 | | // x + (1/3)x^3 + (2/15)x^5 + (17/315)x^7 + (62/2835)x^9 + (1382/155925)x^11 |
556 | | // |
557 | | // (Generated by https://www.wolframalpha.com/widgets/view.jsp?id=fe1ad8d4f5dbb3cb866d0c89beb527a6) |
558 | | // The more terms, the better the accuracy. This generates accuracy within approx 10^-8 for angles |
559 | | // less than PI/8. |
560 | | const SERIES: [(Decimal, u64); 6] = [ |
561 | | // 1 / 3 |
562 | | (Decimal::from_parts_raw(89478485, 347537611, 180700362, 1835008), 3), |
563 | | // 2 / 15 |
564 | | (Decimal::from_parts_raw(894784853, 3574988881, 72280144, 1835008), 5), |
565 | | // 17 / 315 |
566 | | (Decimal::from_parts_raw(905437054, 3907911371, 2925624, 1769472), 7), |
567 | | // 62 / 2835 |
568 | | (Decimal::from_parts_raw(3191872741, 2108928381, 11855473, 1835008), 9), |
569 | | // 1382 / 155925 |
570 | | (Decimal::from_parts_raw(3482645539, 2612995122, 4804769, 1835008), 11), |
571 | | // 21844 / 6081075 |
572 | | (Decimal::from_parts_raw(4189029078, 2192791200, 1947296, 1835008), 13), |
573 | | ]; |
574 | 0 | let mut result = *self; |
575 | 0 | for (fraction, pow) in SERIES { |
576 | 0 | result += fraction * self.powu(pow); |
577 | 0 | } |
578 | 0 | Some(result) |
579 | 0 | } |
580 | | } |
581 | | |
582 | | impl Pow<Decimal> for Decimal { |
583 | | type Output = Decimal; |
584 | | |
585 | 0 | fn pow(self, rhs: Decimal) -> Self::Output { |
586 | 0 | MathematicalOps::powd(&self, rhs) |
587 | 0 | } |
588 | | } |
589 | | |
590 | | impl Pow<u64> for Decimal { |
591 | | type Output = Decimal; |
592 | | |
593 | 0 | fn pow(self, rhs: u64) -> Self::Output { |
594 | 0 | MathematicalOps::powu(&self, rhs) |
595 | 0 | } |
596 | | } |
597 | | |
598 | | impl Pow<i64> for Decimal { |
599 | | type Output = Decimal; |
600 | | |
601 | 0 | fn pow(self, rhs: i64) -> Self::Output { |
602 | 0 | MathematicalOps::powi(&self, rhs) |
603 | 0 | } |
604 | | } |
605 | | |
606 | | impl Pow<f64> for Decimal { |
607 | | type Output = Decimal; |
608 | | |
609 | 0 | fn pow(self, rhs: f64) -> Self::Output { |
610 | 0 | MathematicalOps::powf(&self, rhs) |
611 | 0 | } |
612 | | } |
613 | | |
614 | | #[cfg(test)] |
615 | | mod test { |
616 | | use super::*; |
617 | | |
618 | | #[cfg(not(feature = "std"))] |
619 | | use alloc::string::ToString; |
620 | | |
621 | | #[test] |
622 | | fn test_factorials() { |
623 | | assert_eq!("1", FACTORIAL[0].to_string(), "0!"); |
624 | | assert_eq!("1", FACTORIAL[1].to_string(), "1!"); |
625 | | assert_eq!("2", FACTORIAL[2].to_string(), "2!"); |
626 | | assert_eq!("6", FACTORIAL[3].to_string(), "3!"); |
627 | | assert_eq!("24", FACTORIAL[4].to_string(), "4!"); |
628 | | assert_eq!("120", FACTORIAL[5].to_string(), "5!"); |
629 | | assert_eq!("720", FACTORIAL[6].to_string(), "6!"); |
630 | | assert_eq!("5040", FACTORIAL[7].to_string(), "7!"); |
631 | | assert_eq!("40320", FACTORIAL[8].to_string(), "8!"); |
632 | | assert_eq!("362880", FACTORIAL[9].to_string(), "9!"); |
633 | | assert_eq!("3628800", FACTORIAL[10].to_string(), "10!"); |
634 | | assert_eq!("39916800", FACTORIAL[11].to_string(), "11!"); |
635 | | assert_eq!("479001600", FACTORIAL[12].to_string(), "12!"); |
636 | | assert_eq!("6227020800", FACTORIAL[13].to_string(), "13!"); |
637 | | assert_eq!("87178291200", FACTORIAL[14].to_string(), "14!"); |
638 | | assert_eq!("1307674368000", FACTORIAL[15].to_string(), "15!"); |
639 | | assert_eq!("20922789888000", FACTORIAL[16].to_string(), "16!"); |
640 | | assert_eq!("355687428096000", FACTORIAL[17].to_string(), "17!"); |
641 | | assert_eq!("6402373705728000", FACTORIAL[18].to_string(), "18!"); |
642 | | assert_eq!("121645100408832000", FACTORIAL[19].to_string(), "19!"); |
643 | | assert_eq!("2432902008176640000", FACTORIAL[20].to_string(), "20!"); |
644 | | assert_eq!("51090942171709440000", FACTORIAL[21].to_string(), "21!"); |
645 | | assert_eq!("1124000727777607680000", FACTORIAL[22].to_string(), "22!"); |
646 | | assert_eq!("25852016738884976640000", FACTORIAL[23].to_string(), "23!"); |
647 | | assert_eq!("620448401733239439360000", FACTORIAL[24].to_string(), "24!"); |
648 | | assert_eq!("15511210043330985984000000", FACTORIAL[25].to_string(), "25!"); |
649 | | assert_eq!("403291461126605635584000000", FACTORIAL[26].to_string(), "26!"); |
650 | | assert_eq!("10888869450418352160768000000", FACTORIAL[27].to_string(), "27!"); |
651 | | } |
652 | | } |