bigdecimal 4.1.1 → 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.
@@ -144,7 +144,7 @@ module BigMath
144
144
  x = -x if neg = x < 0
145
145
  ex = x.exponent / 3
146
146
  x = x._decimal_shift(-3 * ex)
147
- y = BigDecimal(Math.cbrt(BigDecimal::Internal.fast_to_f(x)), 0)
147
+ y = BigDecimal(Math.cbrt(x.to_f), 0)
148
148
  BigDecimal::Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
149
149
  y = (2 * y + x.div(y, p).div(y, p)).div(3, p)
150
150
  end
@@ -304,7 +304,7 @@ module BigMath
304
304
 
305
305
  # Solve tan(y) - x = 0 with Newton's method
306
306
  # Repeat: y -= (tan(y) - x) * cos(y)**2
307
- y = BigDecimal(Math.atan(BigDecimal::Internal.fast_to_f(x)), 0)
307
+ y = BigDecimal(Math.atan(x.to_f), 0)
308
308
  BigDecimal::Internal.newton_loop(n) do |p|
309
309
  s = sin(y, p)
310
310
  c = (1 - s * s).sqrt(p)
@@ -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 = BigDecimal::Internal.fast_to_f(x)
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(0) 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 = BigDecimal::Internal.fast_to_f(x)
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 = BigDecimal::Internal.fast_to_f(x)
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/lib/bigdecimal.rb CHANGED
@@ -57,6 +57,13 @@ class BigDecimal
57
57
  BigDecimal::INFINITY
58
58
  end
59
59
 
60
+ def self.underflow_computation_result # :nodoc:
61
+ if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_UNDERFLOW)
62
+ raise FloatDomainError, 'Exponent underflow'
63
+ end
64
+ BigDecimal(0)
65
+ end
66
+
60
67
  def self.nan_computation_result # :nodoc:
61
68
  if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_NaN)
62
69
  raise FloatDomainError, "Computation results to 'NaN'"
@@ -76,18 +83,9 @@ class BigDecimal
76
83
  end
77
84
  end
78
85
 
79
- # Fast and rough conversion to float for mathematical calculations.
80
- # Bigdecimal#to_f is slow when n_significant_digits is large.
81
- # This is because to_f internally converts BigDecimal to String
82
- # to get the exact nearest float representation.
83
- # TODO: Remove this workaround when BigDecimal#to_f is optimized.
84
- def self.fast_to_f(x) # :nodoc:
85
- x.n_significant_digits < 40 ? x.to_f : x.mult(1, 20).to_f
86
- end
87
-
88
86
  # Calculates Math.log(x.to_f) considering large or small exponent
89
87
  def self.float_log(x) # :nodoc:
90
- Math.log(fast_to_f(x._decimal_shift(-x.exponent))) + x.exponent * Math.log(10)
88
+ Math.log(x._decimal_shift(-x.exponent).to_f) + x.exponent * Math.log(10)
91
89
  end
92
90
 
93
91
  # Calculating Taylor series sum using binary splitting method
@@ -277,7 +275,7 @@ class BigDecimal
277
275
 
278
276
  ex = exponent / 2
279
277
  x = _decimal_shift(-2 * ex)
280
- y = BigDecimal(Math.sqrt(BigDecimal::Internal.fast_to_f(x)), 0)
278
+ y = BigDecimal(Math.sqrt(x.to_f), 0)
281
279
  Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
282
280
  y = y.add(x.div(y, p), p).div(2, p)
283
281
  end
@@ -359,7 +357,17 @@ module BigMath
359
357
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :exp)
360
358
  x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :exp)
361
359
  return BigDecimal::Internal.nan_computation_result if x.nan?
362
- return x.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) if x.infinite?
360
+ if x.infinite? || x.exponent >= 21 # exp(10**20) and exp(-10**20) overflows/underflows 64-bit exponent
361
+ if x.positive?
362
+ return BigDecimal::Internal.infinity_computation_result
363
+ elsif x.infinite?
364
+ # exp(-Infinity) is +0 by definition, this is not an underflow.
365
+ return BigDecimal(0)
366
+ else
367
+ return BigDecimal::Internal.underflow_computation_result
368
+ end
369
+ end
370
+
363
371
  return BigDecimal(1) if x.zero?
364
372
 
365
373
  # exp(x * 10**cnt) = exp(x)**(10**cnt)
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.1
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