bigdecimal 4.1.2 → 4.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -596,28 +596,8 @@ module BigMath
596
596
  # #=> "0.84270079294971486934122063508261e0"
597
597
  #
598
598
  def erf(x, prec)
599
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf)
600
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf)
601
- return BigDecimal::Internal.nan_computation_result if x.nan?
602
- return BigDecimal(x.infinite?) if x.infinite?
603
- return BigDecimal(0) if x == 0
604
- return -erf(-x, prec) if x < 0
605
- return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
606
-
607
- if x > 8
608
- xf = x.to_f
609
- log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
610
- erfc_prec = [prec + log10_erfc.ceil, 1].max
611
- erfc = _erfc_asymptotic(x, erfc_prec)
612
- return BigDecimal(1).sub(erfc, prec) if erfc
613
- end
614
-
615
- prec2 = prec + BigDecimal::Internal::EXTRA_PREC
616
- x_smallprec = x.mult(1, Integer.sqrt(prec2) / 2)
617
- # Taylor series of x with small precision is fast
618
- erf1 = _erf_taylor(x_smallprec, BigDecimal(0), BigDecimal(0), prec2)
619
- # Taylor series converges quickly for small x
620
- _erf_taylor(x - x_smallprec, x_smallprec, erf1, prec2).mult(1, prec)
599
+ require 'bigdecimal/math/erf'
600
+ Erf.erf(x, prec)
621
601
  end
622
602
 
623
603
  # call-seq:
@@ -632,87 +612,8 @@ module BigMath
632
612
  # #=> "0.20884875837625447570007862949578e-44"
633
613
  #
634
614
  def erfc(x, prec)
635
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc)
636
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc)
637
- return BigDecimal::Internal.nan_computation_result if x.nan?
638
- return BigDecimal(1 - x.infinite?) if x.infinite?
639
- return BigDecimal(1).sub(erf(x, prec + BigDecimal::Internal::EXTRA_PREC), prec) if x < 0.5
640
- return BigDecimal::Internal.underflow_computation_result if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
641
-
642
- if x >= 8
643
- y = _erfc_asymptotic(x, prec)
644
- return y.mult(1, prec) if y
645
- end
646
-
647
- # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
648
- # Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
649
- log10 = 2.302585092994046
650
- xf = x.to_f
651
- high_prec = prec + BigDecimal::Internal::EXTRA_PREC + ((xf**2 + Math.log(xf) + Math.log(Math::PI)/2) / log10).ceil
652
- BigDecimal(1).sub(erf(x, high_prec), prec)
653
- end
654
-
655
- # Calculates erf(x + a)
656
- private_class_method def _erf_taylor(x, a, erf_a, prec) # :nodoc:
657
- return erf_a if x.zero?
658
- # Let f(x+a) = erf(x+a)*exp((x+a)**2)*sqrt(pi)/2
659
- # = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...
660
- # f'(x+a) = 1+2*(x+a)*f(x+a)
661
- # f'(x+a) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ...
662
- # = 1+2*(x+a)*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...)
663
- # therefore,
664
- # c0 = f(a)
665
- # c1 = 2 * a * c0 + 1
666
- # c2 = (2 * c0 + 2 * a * c1) / 2
667
- # c3 = (2 * c1 + 2 * a * c2) / 3
668
- # c4 = (2 * c2 + 2 * a * c3) / 4
669
- #
670
- # All coefficients are positive when a >= 0
671
-
672
- scale = BigDecimal(2).div(sqrt(PI(prec), prec), prec)
673
- c_prev = erf_a.div(scale.mult(exp(-a*a, prec), prec), prec)
674
- c_next = (2 * a * c_prev).add(1, prec).mult(x, prec)
675
- sum = c_prev.add(c_next, prec)
676
-
677
- 2.step do |k|
678
- cn = (c_prev.mult(x, prec) + a * c_next).mult(2, prec).mult(x, prec).div(k, prec)
679
- sum = sum.add(cn, prec)
680
- c_prev, c_next = c_next, cn
681
- break if [c_prev, c_next].all? { |c| c.zero? || (c.exponent < sum.exponent - prec) }
682
- end
683
- value = sum.mult(scale.mult(exp(-(x + a).mult(x + a, prec), prec), prec), prec)
684
- value > 1 ? BigDecimal(1) : value
685
- end
686
-
687
- private_class_method def _erfc_asymptotic(x, prec) # :nodoc:
688
- # Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2
689
- # f(x) satisfies the following differential equation:
690
- # 2*x*f(x) = f'(x) + 1
691
- # From the above equation, we can derive the following asymptotic expansion:
692
- # f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k)) } / x
693
-
694
- # This asymptotic expansion does not converge.
695
- # But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec),
696
- # It is enough to calculate erfc within the given precision.
697
- # Using Stirling's approximation, we can simplify this condition to:
698
- # sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
699
- # and the left side is minimized when k = x**2.
700
- prec += BigDecimal::Internal::EXTRA_PREC
701
- xf = x.to_f
702
- kmax = (1..(xf ** 2).floor).bsearch do |k|
703
- Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
704
- end
705
- return unless kmax
706
-
707
- sum = BigDecimal(1)
708
- # To calculate `exp(x2, prec)`, x2 needs extra log10(x**2) digits of precision
709
- x2 = x.mult(x, prec + (2 * Math.log10(xf)).ceil)
710
- d = BigDecimal(1)
711
- (1..kmax).each do |k|
712
- d = d.div(x2, prec).mult(1 - 2 * k, prec).div(2, prec)
713
- sum = sum.add(d, prec)
714
- end
715
- sum.div(exp(x2, prec).mult(PI(prec).sqrt(prec), prec), prec).div(x, prec)
615
+ require 'bigdecimal/math/erf'
616
+ Erf.erfc(x, prec)
716
617
  end
717
618
 
718
619
  # call-seq:
@@ -725,22 +626,8 @@ module BigMath
725
626
  # #=> "0.17724538509055160272981674833411e1"
726
627
  #
727
628
  def gamma(x, prec)
728
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :gamma)
729
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :gamma)
730
- prec2 = prec + BigDecimal::Internal::EXTRA_PREC
731
- if x < 0.5
732
- raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero?
733
-
734
- # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
735
- pi = PI(prec2)
736
- sin = _sinpix(x, pi, prec2)
737
- return pi.div(gamma(1 - x, prec2).mult(sin, prec2), prec)
738
- elsif x.frac.zero? && x < 1000 * prec
739
- return _gamma_positive_integer(x, prec2).mult(1, prec)
740
- end
741
-
742
- a, sum = _gamma_spouge_sum_part(x, prec2)
743
- (x + (a - 1)).power(x - 0.5, prec2).mult(BigMath.exp(1 - x, prec2), prec2).mult(sum, prec)
629
+ require 'bigdecimal/math/gamma'
630
+ Gamma.gamma(x, prec)
744
631
  end
745
632
 
746
633
  # call-seq:
@@ -753,106 +640,8 @@ module BigMath
753
640
  # #=> [0.57236494292470008707171367567653e0, 1]
754
641
  #
755
642
  def lgamma(x, prec)
756
- prec = BigDecimal::Internal.coerce_validate_prec(prec, :lgamma)
757
- x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :lgamma)
758
- prec2 = prec + BigDecimal::Internal::EXTRA_PREC
759
- if x < 0.5
760
- return [BigDecimal::INFINITY, 1] if x.frac.zero?
761
-
762
- loop do
763
- # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
764
- pi = PI(prec2)
765
- sin = _sinpix(x, pi, prec2)
766
- log_gamma = BigMath.log(pi, prec2).sub(lgamma(1 - x, prec2).first + BigMath.log(sin.abs, prec2), prec)
767
- return [log_gamma, sin > 0 ? 1 : -1] if prec2 + log_gamma.exponent > prec + BigDecimal::Internal::EXTRA_PREC
768
-
769
- # Retry with higher precision if loss of significance is too large
770
- prec2 = prec2 * 3 / 2
771
- end
772
- elsif x.frac.zero? && x < 1000 * prec
773
- log_gamma = BigMath.log(_gamma_positive_integer(x, prec2), prec)
774
- [log_gamma, 1]
775
- else
776
- # if x is close to 1 or 2, increase precision to reduce loss of significance
777
- diff1_exponent = (x - 1).exponent
778
- diff2_exponent = (x - 2).exponent
779
- extremely_near_one = diff1_exponent < -prec2
780
- extremely_near_two = diff2_exponent < -prec2
781
-
782
- if extremely_near_one || extremely_near_two
783
- # If x is extreamely close to base = 1 or 2, linear interpolation is accurate enough.
784
- # Taylor expansion at x = base is: (x - base) * digamma(base) + (x - base) ** 2 * trigamma(base) / 2 + ...
785
- # And we can ignore (x - base) ** 2 and higher order terms.
786
- base = extremely_near_one ? 1 : 2
787
- d = BigDecimal(1)._decimal_shift(1 - prec2)
788
- log_gamma_d, sign = lgamma(base + d, prec2)
789
- return [log_gamma_d.mult(x - base, prec2).div(d, prec), sign]
790
- end
791
-
792
- prec2 += [-diff1_exponent, -diff2_exponent, 0].max
793
- a, sum = _gamma_spouge_sum_part(x, prec2)
794
- log_gamma = BigMath.log(sum, prec2).add((x - 0.5).mult(BigMath.log(x.add(a - 1, prec2), prec2), prec2) + 1 - x, prec)
795
- [log_gamma, 1]
796
- end
797
- end
798
-
799
- # Returns sum part: sqrt(2*pi) and c[k]/(x+k) terms of Spouge's approximation
800
- private_class_method def _gamma_spouge_sum_part(x, prec) # :nodoc:
801
- x -= 1
802
- # Spouge's approximation
803
- # x! = (x + a)**(x + 0.5) * exp(-x - a) * (sqrt(2 * pi) + (1..a - 1).sum{|k| c[k] / (x + k) } + epsilon)
804
- # where c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(a - k) / (k - 1)!
805
- # and epsilon is bounded by a**(-0.5) * (2 * pi) ** (-a - 0.5)
806
-
807
- # Estimate required a for given precision
808
- a = (prec / Math.log10(2 * Math::PI)).ceil
809
-
810
- # Calculate exponent of c[k] in low precision to estimate required precision
811
- low_prec = 16
812
- log10f = Math.log(10)
813
- x_low_prec = x.mult(1, low_prec)
814
- loggamma_k = 0
815
- ck_exponents = (1..a-1).map do |k|
816
- loggamma_k += Math.log10(k - 1) if k > 1
817
- -loggamma_k - k / log10f + (k - 0.5) * Math.log10(a - k) - BigDecimal::Internal.float_log(x_low_prec.add(k, low_prec)) / log10f
818
- end
819
-
820
- # Estimate exponent of sum by Stirling's approximation
821
- approx_sum_exponent = x < 1 ? -Math.log10(a) / 2 : Math.log10(2 * Math::PI) / 2 + x_low_prec.add(0.5, low_prec) * Math.log10(x_low_prec / x_low_prec.add(a, low_prec))
822
-
823
- # Determine required precision of c[k]
824
- prec2 = [ck_exponents.max.ceil - approx_sum_exponent.floor, 0].max + prec
825
-
826
- einv = BigMath.exp(-1, prec2)
827
- sum = (PI(prec) * 2).sqrt(prec).mult(BigMath.exp(-a, prec), prec)
828
- y = BigDecimal(1)
829
- (1..a - 1).each do |k|
830
- # c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(-k) / (k-1)! / (x + k)
831
- y = y.div(1 - k, prec2) if k > 1
832
- y = y.mult(einv, prec2)
833
- z = y.mult(BigDecimal((a - k) ** k), prec2).div(BigDecimal(a - k).sqrt(prec2).mult(x.add(k, prec2), prec2), prec2)
834
- # sum += c[k] / (x + k)
835
- sum = sum.add(z, prec2)
836
- end
837
- [a, sum]
838
- end
839
-
840
- private_class_method def _gamma_positive_integer(x, prec) # :nodoc:
841
- return x if x == 1
842
- numbers = (1..x - 1).map {|i| BigDecimal(i) }
843
- while numbers.size > 1
844
- numbers = numbers.each_slice(2).map {|a, b| b ? a.mult(b, prec) : a }
845
- end
846
- numbers.first
847
- end
848
-
849
- # Returns sin(pi * x), for gamma reflection formula calculation
850
- private_class_method def _sinpix(x, pi, prec) # :nodoc:
851
- x = x % 2
852
- sign = x > 1 ? -1 : 1
853
- x %= 1
854
- x = 1 - x if x > 0.5 # to avoid sin(pi*x) loss of precision for x close to 1
855
- sign * sin(x.mult(pi, prec), prec)
643
+ require 'bigdecimal/math/gamma'
644
+ Gamma.lgamma(x, prec)
856
645
  end
857
646
 
858
647
  # call-seq:
data/sig/big_decimal.rbs CHANGED
@@ -1237,7 +1237,9 @@ module Kernel
1237
1237
  # Raises an exception if `value` evaluates to a Float and `digits` is larger
1238
1238
  # than Float::DIG + 1.
1239
1239
  #
1240
- def self?.BigDecimal: (real | string | BigDecimal initial, ?int digits, ?exception: bool) -> BigDecimal
1240
+ def self?.BigDecimal: (real | string | BigDecimal initial, ?int digits, ?exception: true) -> BigDecimal
1241
+ | (real | string | BigDecimal initial, ?int digits, exception: bool) -> BigDecimal?
1242
+ | (untyped initial, ?untyped digits, ?exception: bool) -> BigDecimal?
1241
1243
  end
1242
1244
 
1243
1245
  %a{annotate:rdoc:skip}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bigdecimal
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.2
4
+ version: 4.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
@@ -37,6 +37,8 @@ files:
37
37
  - lib/bigdecimal/jacobian.rb
38
38
  - lib/bigdecimal/ludcmp.rb
39
39
  - lib/bigdecimal/math.rb
40
+ - lib/bigdecimal/math/erf.rb
41
+ - lib/bigdecimal/math/gamma.rb
40
42
  - lib/bigdecimal/newton.rb
41
43
  - lib/bigdecimal/util.rb
42
44
  - sample/linear.rb