|
24 | 24 | # log10(x, prec) |
25 | 25 | # log1p(x, prec) |
26 | 26 | # expm1(x, prec) |
| 27 | +# erf (x, prec) |
| 28 | +# erfc(x, prec) |
27 | 29 | # PI (prec) |
28 | 30 | # E (prec) == exp(1.0,prec) |
29 | 31 | # |
@@ -568,6 +570,129 @@ def expm1(x, prec) |
568 | 570 | exp_prec > 0 ? exp(x, exp_prec).sub(1, prec) : BigDecimal(-1) |
569 | 571 | end |
570 | 572 |
|
| 573 | + # call-seq: |
| 574 | + # erf(decimal, numeric) -> BigDecimal |
| 575 | + # |
| 576 | + # Computes the error function of +decimal+ to the specified number of digits of |
| 577 | + # precision, +numeric+. |
| 578 | + # |
| 579 | + # If +decimal+ is NaN, returns NaN. |
| 580 | + # |
| 581 | + # BigMath.erf(BigDecimal('1'), 32).to_s |
| 582 | + # #=> "0.84270079294971486934122063508261e0" |
| 583 | + # |
| 584 | + def erf(x, prec) |
| 585 | + prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf) |
| 586 | + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf) |
| 587 | + return BigDecimal::Internal.nan_computation_result if x.nan? |
| 588 | + return BigDecimal(x.infinite?) if x.infinite? |
| 589 | + return BigDecimal(0) if x == 0 |
| 590 | + return -erf(-x, prec) if x < 0 |
| 591 | + |
| 592 | + if x > 8 && (erfc1 = _erfc_asymptotic(x, 1)) |
| 593 | + erfc2 = _erfc_asymptotic(x, [prec + erfc1.exponent, 1].max) |
| 594 | + return BigDecimal(1).sub(erfc2, prec) if erfc2 |
| 595 | + end |
| 596 | + |
| 597 | + prec2 = prec + BigDecimal.double_fig |
| 598 | + x_smallprec = x.mult(1, Integer.sqrt(prec2) / 2) |
| 599 | + # Taylor series of x with small precision is fast |
| 600 | + erf1 = _erf_taylor(x_smallprec, BigDecimal(0), BigDecimal(0), prec2) |
| 601 | + # Taylor series converges quickly for small x |
| 602 | + _erf_taylor(x - x_smallprec, x_smallprec, erf1, prec2).mult(1, prec) |
| 603 | + end |
| 604 | + |
| 605 | + # call-seq: |
| 606 | + # erfc(decimal, numeric) -> BigDecimal |
| 607 | + # |
| 608 | + # Computes the complementary error function of +decimal+ to the specified number of digits of |
| 609 | + # precision, +numeric+. |
| 610 | + # |
| 611 | + # If +decimal+ is NaN, returns NaN. |
| 612 | + # |
| 613 | + # BigMath.erfc(BigDecimal('10'), 32).to_s |
| 614 | + # #=> "0.20884875837625447570007862949578e-44" |
| 615 | + # |
| 616 | + def erfc(x, prec) |
| 617 | + prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc) |
| 618 | + x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc) |
| 619 | + return BigDecimal::Internal.nan_computation_result if x.nan? |
| 620 | + return BigDecimal(1 - x.infinite?) if x.infinite? |
| 621 | + return BigDecimal(1).sub(erf(x, prec), prec) if x < 0 |
| 622 | + |
| 623 | + if x >= 8 |
| 624 | + y = _erfc_asymptotic(x, prec) |
| 625 | + return y.mult(1, prec) if y |
| 626 | + end |
| 627 | + |
| 628 | + # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi) |
| 629 | + # Precision of erf(x) needs about log10(exp(-x**2)) extra digits |
| 630 | + log10 = 2.302585092994046 |
| 631 | + high_prec = prec + BigDecimal.double_fig + (x**2 / log10).ceil |
| 632 | + BigDecimal(1).sub(erf(x, high_prec), prec) |
| 633 | + end |
| 634 | + |
| 635 | + # Calculates erf(x + a) |
| 636 | + private_class_method def _erf_taylor(x, a, erf_a, prec) # :nodoc: |
| 637 | + # Let f(x) = erf(x+a)*exp((x+a)**2)*sqrt(pi)/2 |
| 638 | + # = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ... |
| 639 | + # f'(x) = 1+2*(x+a)*f(x) |
| 640 | + # f'(x) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ... |
| 641 | + # = 1+2*(x+a)*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...) |
| 642 | + # therefore, |
| 643 | + # c0 = f(0) |
| 644 | + # c1 = 2 * a * c0 + 1 |
| 645 | + # c2 = (2 * c0 + 2 * a * c1) / 2 |
| 646 | + # c3 = (2 * c1 + 2 * a * c2) / 3 |
| 647 | + # c4 = (2 * c2 + 2 * a * c3) / 4 |
| 648 | + |
| 649 | + return erf_a if x.zero? |
| 650 | + |
| 651 | + scale = BigDecimal(2).div(sqrt(PI(prec), prec), prec) |
| 652 | + c_prev = erf_a.div(scale.mult(exp(-a*a, prec), prec), prec) |
| 653 | + c_next = (2 * a * c_prev).add(1, prec).mult(x, prec) |
| 654 | + v = c_prev.add(c_next, prec) |
| 655 | + |
| 656 | + 2.step do |k| |
| 657 | + c = (c_prev.mult(x, prec) + a * c_next).mult(2, prec).mult(x, prec).div(k, prec) |
| 658 | + v = v.add(c, prec) |
| 659 | + c_prev, c_next = c_next, c |
| 660 | + break if [c_prev, c_next].all? { |c| c.zero? || (c.exponent < v.exponent - prec) } |
| 661 | + end |
| 662 | + v = v.mult(scale.mult(exp(-(x + a).mult(x + a, prec), prec), prec), prec) |
| 663 | + v > 1 ? BigDecimal(1) : v |
| 664 | + end |
| 665 | + |
| 666 | + private_class_method def _erfc_asymptotic(x, prec) # :nodoc: |
| 667 | + # Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2 |
| 668 | + # f(x) satisfies the following differential equation: |
| 669 | + # 2*x*f(x) = f'(x) + 1 |
| 670 | + # From the above equation, we can derive the following asymptotic expansion: |
| 671 | + # f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k)) } / x |
| 672 | + |
| 673 | + # This asymptotic expansion does not converge. |
| 674 | + # But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec), |
| 675 | + # It is enough to calculate erfc within the given precision. |
| 676 | + # (2*k)! / 4**k / k! can be approximated as sqrt(2) * (k/e)**k by using Stirling's approximation. |
| 677 | + prec += BigDecimal.double_fig |
| 678 | + xf = x.to_f |
| 679 | + log10xf = Math.log10(xf) |
| 680 | + kmax = 1 |
| 681 | + until kmax * Math.log10(kmax / Math::E) + 1 - 2 * kmax * log10xf < -prec |
| 682 | + kmax += 1 |
| 683 | + return if xf * xf < kmax # Unable to calculate with the given precision |
| 684 | + end |
| 685 | + |
| 686 | + sum = BigDecimal(1) |
| 687 | + x2 = x.mult(x, prec) |
| 688 | + d = BigDecimal(1) |
| 689 | + (1..kmax).each do |k| |
| 690 | + d = d.div(x2, prec).mult(1 - 2 * k, prec).div(2, prec) |
| 691 | + sum = sum.add(d, prec) |
| 692 | + end |
| 693 | + expx2 = exp(x.mult(x, prec), prec) |
| 694 | + sum.div(expx2.mult(PI(prec).sqrt(prec), prec), prec).div(x, prec) |
| 695 | + end |
571 | 696 |
|
572 | 697 | # call-seq: |
573 | 698 | # PI(numeric) -> BigDecimal |
|
0 commit comments