bigdecimal 3.2.2-java → 3.3.0-java

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 747f7c4c4370b7ef90984fd667857671fdd40d779d69b7f074e40b6db23a642c
4
- data.tar.gz: 8422501bf2de0f2003f92bd7449614203258906d8a0d9027890c416ef8ca5513
3
+ metadata.gz: 7b3e1def7d1cfc2c72b5aa0a460ef768973e6c704a1225663e8df144718ac23a
4
+ data.tar.gz: 41e414cf6a1348e4567ce0b06928abb2c757a3a4f5de05ebcb1d78ff54140afe
5
5
  SHA512:
6
- metadata.gz: 8ad8f50670b92f8426f422a27a9e4f84b8a9f6ea8252e9dba1f9cd51f349664e22d21939fd9e90a0d916bb46f065deae86969c319902a1f4c26819d68f4e6d27
7
- data.tar.gz: '080e4d21538a3254ee4acd5671c63807e837c707f1b5afa6739fbc1b928ea9b2032575021257edbd891c105f64587fd3c871be7c82684c4c1732c9928af9d876'
6
+ metadata.gz: c5880f8f1e8ed360116110107cf448894118a6714ecc916b3be74c056dab9e258b82620db42ace5a4b0b5833445859403e401fd2ba3a6c7d5aecaea1068986da
7
+ data.tar.gz: 33d209590eaa3132f31495884df9a5f08eae7382d349df1977c03b69d6e48c3a3bab0dee6fc5c99ead0690e5dee4f74364976d99381290b7c71f0ab4e93d7f07
@@ -7,6 +7,7 @@ require 'bigdecimal'
7
7
  # sqrt(x, prec)
8
8
  # sin (x, prec)
9
9
  # cos (x, prec)
10
+ # tan (x, prec)
10
11
  # atan(x, prec)
11
12
  # PI (prec)
12
13
  # E (prec) == exp(1.0,prec)
@@ -24,8 +25,8 @@ require 'bigdecimal'
24
25
  #
25
26
  # include BigMath
26
27
  #
27
- # a = BigDecimal((PI(100)/2).to_s)
28
- # puts sin(a,100) # => 0.99999999999999999999......e0
28
+ # a = BigDecimal((PI(49)/2).to_s)
29
+ # puts sin(a,100) # => 0.9999999999...9999999986e0
29
30
  #
30
31
  module BigMath
31
32
  module_function
@@ -36,13 +37,42 @@ module BigMath
36
37
  # Computes the square root of +decimal+ to the specified number of digits of
37
38
  # precision, +numeric+.
38
39
  #
39
- # BigMath.sqrt(BigDecimal('2'), 16).to_s
40
- # #=> "0.1414213562373095048801688724e1"
40
+ # BigMath.sqrt(BigDecimal('2'), 32).to_s
41
+ # #=> "0.14142135623730950488016887242097e1"
41
42
  #
42
43
  def sqrt(x, prec)
44
+ BigDecimal::Internal.validate_prec(prec, :sqrt)
45
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sqrt)
43
46
  x.sqrt(prec)
44
47
  end
45
48
 
49
+
50
+ # Returns [sign, reduced_x] where reduced_x is in -pi/2..pi/2
51
+ # and satisfies sin(x) = sign * sin(reduced_x)
52
+ # If add_half_pi is true, adds pi/2 to x before reduction.
53
+ # Precision of pi is adjusted to ensure reduced_x has the required precision.
54
+ private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc:
55
+ return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi
56
+
57
+ mod_prec = prec + BigDecimal.double_fig
58
+ pi_extra_prec = [x.exponent, 0].max + BigDecimal.double_fig
59
+ while true
60
+ pi = PI(mod_prec + pi_extra_prec)
61
+ half_pi = pi / 2
62
+ div, mod = (add_half_pi ? x + pi : x + half_pi).divmod(pi)
63
+ mod -= half_pi
64
+ if mod.zero? || mod_prec + mod.exponent <= 0
65
+ # mod is too small to estimate required pi precision
66
+ mod_prec = mod_prec * 3 / 2 + BigDecimal.double_fig
67
+ elsif mod_prec + mod.exponent < prec
68
+ # Estimate required precision of pi
69
+ mod_prec = prec - mod.exponent + BigDecimal.double_fig
70
+ else
71
+ return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
72
+ end
73
+ end
74
+ end
75
+
46
76
  # call-seq:
47
77
  # sin(decimal, numeric) -> BigDecimal
48
78
  #
@@ -51,40 +81,33 @@ module BigMath
51
81
  #
52
82
  # If +decimal+ is Infinity or NaN, returns NaN.
53
83
  #
54
- # BigMath.sin(BigMath.PI(5)/4, 5).to_s
55
- # #=> "0.70710678118654752440082036563292800375e0"
84
+ # BigMath.sin(BigMath.PI(5)/4, 32).to_s
85
+ # #=> "0.70710807985947359435812921837984e0"
56
86
  #
57
87
  def sin(x, prec)
58
- raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
59
- return BigDecimal("NaN") if x.infinite? || x.nan?
88
+ BigDecimal::Internal.validate_prec(prec, :sin)
89
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
90
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
60
91
  n = prec + BigDecimal.double_fig
61
92
  one = BigDecimal("1")
62
93
  two = BigDecimal("2")
63
- x = -x if neg = x < 0
64
- if x > (twopi = two * BigMath.PI(prec))
65
- if x > 30
66
- x %= twopi
67
- else
68
- x -= twopi while x > twopi
69
- end
70
- end
94
+ sign, x = _sin_periodic_reduction(x, n)
71
95
  x1 = x
72
96
  x2 = x.mult(x,n)
73
- sign = 1
74
97
  y = x
75
98
  d = y
76
99
  i = one
77
100
  z = one
78
101
  while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
79
102
  m = BigDecimal.double_fig if m < BigDecimal.double_fig
80
- sign = -sign
81
- x1 = x2.mult(x1,n)
103
+ x1 = -x2.mult(x1,n)
82
104
  i += two
83
105
  z *= (i-one) * i
84
- d = sign * x1.div(z,m)
106
+ d = x1.div(z,m)
85
107
  y += d
86
108
  end
87
- neg ? -y : y
109
+ y = BigDecimal("1") if y > 1
110
+ y.mult(sign, prec)
88
111
  end
89
112
 
90
113
  # call-seq:
@@ -95,40 +118,34 @@ module BigMath
95
118
  #
96
119
  # If +decimal+ is Infinity or NaN, returns NaN.
97
120
  #
98
- # BigMath.cos(BigMath.PI(4), 16).to_s
99
- # #=> "-0.999999999999999999999999999999856613163740061349e0"
121
+ # BigMath.cos(BigMath.PI(16), 32).to_s
122
+ # #=> "-0.99999999999999999999999999999997e0"
100
123
  #
101
124
  def cos(x, prec)
102
- raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
103
- return BigDecimal("NaN") if x.infinite? || x.nan?
104
- n = prec + BigDecimal.double_fig
105
- one = BigDecimal("1")
106
- two = BigDecimal("2")
107
- x = -x if x < 0
108
- if x > (twopi = two * BigMath.PI(prec))
109
- if x > 30
110
- x %= twopi
111
- else
112
- x -= twopi while x > twopi
113
- end
114
- end
115
- x1 = one
116
- x2 = x.mult(x,n)
117
- sign = 1
118
- y = one
119
- d = y
120
- i = BigDecimal("0")
121
- z = one
122
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
123
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
124
- sign = -sign
125
- x1 = x2.mult(x1,n)
126
- i += two
127
- z *= (i-one) * i
128
- d = sign * x1.div(z,m)
129
- y += d
130
- end
131
- y
125
+ BigDecimal::Internal.validate_prec(prec, :cos)
126
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
127
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
128
+ sign, x = _sin_periodic_reduction(x, prec + BigDecimal.double_fig, add_half_pi: true)
129
+ sign * sin(x, prec)
130
+ end
131
+
132
+ # call-seq:
133
+ # tan(decimal, numeric) -> BigDecimal
134
+ #
135
+ # Computes the tangent of +decimal+ to the specified number of digits of
136
+ # precision, +numeric+.
137
+ #
138
+ # If +decimal+ is Infinity or NaN, returns NaN.
139
+ #
140
+ # BigMath.tan(BigDecimal("0.0"), 4).to_s
141
+ # #=> "0.0"
142
+ #
143
+ # BigMath.tan(BigMath.PI(24) / 4, 32).to_s
144
+ # #=> "0.99999999999999999999999830836025e0"
145
+ #
146
+ def tan(x, prec)
147
+ BigDecimal::Internal.validate_prec(prec, :tan)
148
+ sin(x, prec + BigDecimal.double_fig).div(cos(x, prec + BigDecimal.double_fig), prec)
132
149
  end
133
150
 
134
151
  # call-seq:
@@ -139,19 +156,20 @@ module BigMath
139
156
  #
140
157
  # If +decimal+ is NaN, returns NaN.
141
158
  #
142
- # BigMath.atan(BigDecimal('-1'), 16).to_s
143
- # #=> "-0.785398163397448309615660845819878471907514682065e0"
159
+ # BigMath.atan(BigDecimal('-1'), 32).to_s
160
+ # #=> "-0.78539816339744830961566084581988e0"
144
161
  #
145
162
  def atan(x, prec)
146
- raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
147
- return BigDecimal("NaN") if x.nan?
148
- pi = PI(prec)
163
+ BigDecimal::Internal.validate_prec(prec, :atan)
164
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
165
+ return BigDecimal::Internal.nan_computation_result if x.nan?
166
+ n = prec + BigDecimal.double_fig
167
+ pi = PI(n)
149
168
  x = -x if neg = x < 0
150
169
  return pi.div(neg ? -2 : 2, prec) if x.infinite?
151
- return pi / (neg ? -4 : 4) if x.round(prec) == 1
152
- x = BigDecimal("1").div(x, prec) if inv = x > 1
153
- x = (-1 + sqrt(1 + x**2, prec))/x if dbl = x > 0.5
154
- n = prec + BigDecimal.double_fig
170
+ return pi.div(neg ? -4 : 4, prec) if x.round(prec) == 1
171
+ x = BigDecimal("1").div(x, n) if inv = x > 1
172
+ x = (-1 + sqrt(1 + x.mult(x, n), n)).div(x, n) if dbl = x > 0.5
155
173
  y = x
156
174
  d = y
157
175
  t = x
@@ -167,7 +185,7 @@ module BigMath
167
185
  y *= 2 if dbl
168
186
  y = pi / 2 - y if inv
169
187
  y = -y if neg
170
- y
188
+ y.mult(1, prec)
171
189
  end
172
190
 
173
191
  # call-seq:
@@ -176,11 +194,11 @@ module BigMath
176
194
  # Computes the value of pi to the specified number of digits of precision,
177
195
  # +numeric+.
178
196
  #
179
- # BigMath.PI(10).to_s
180
- # #=> "0.3141592653589793238462643388813853786957412e1"
197
+ # BigMath.PI(32).to_s
198
+ # #=> "0.31415926535897932384626433832795e1"
181
199
  #
182
200
  def PI(prec)
183
- raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
201
+ BigDecimal::Internal.validate_prec(prec, :PI)
184
202
  n = prec + BigDecimal.double_fig
185
203
  zero = BigDecimal("0")
186
204
  one = BigDecimal("1")
@@ -212,7 +230,7 @@ module BigMath
212
230
  pi = pi + d
213
231
  k = k+two
214
232
  end
215
- pi
233
+ pi.mult(1, prec)
216
234
  end
217
235
 
218
236
  # call-seq:
@@ -221,11 +239,11 @@ module BigMath
221
239
  # Computes e (the base of natural logarithms) to the specified number of
222
240
  # digits of precision, +numeric+.
223
241
  #
224
- # BigMath.E(10).to_s
225
- # #=> "0.271828182845904523536028752390026306410273e1"
242
+ # BigMath.E(32).to_s
243
+ # #=> "0.27182818284590452353602874713527e1"
226
244
  #
227
245
  def E(prec)
228
- raise ArgumentError, "Zero or negative precision for E" if prec <= 0
246
+ BigDecimal::Internal.validate_prec(prec, :E)
229
247
  BigMath.exp(1, prec)
230
248
  end
231
249
  end
@@ -119,8 +119,11 @@ class Rational < Numeric
119
119
  #
120
120
  # Returns the value as a BigDecimal.
121
121
  #
122
- # The required +precision+ parameter is used to determine the number of
123
- # significant digits for the result.
122
+ # The +precision+ parameter is used to determine the number of
123
+ # significant digits for the result. When +precision+ is set to +0+,
124
+ # the number of digits to represent the float being converted is determined
125
+ # automatically.
126
+ # The default +precision+ is +0+.
124
127
  #
125
128
  # require 'bigdecimal'
126
129
  # require 'bigdecimal/util'
@@ -129,7 +132,7 @@ class Rational < Numeric
129
132
  #
130
133
  # See also Kernel.BigDecimal.
131
134
  #
132
- def to_d(precision)
135
+ def to_d(precision=0)
133
136
  BigDecimal(self, precision)
134
137
  end
135
138
  end
@@ -141,29 +144,27 @@ class Complex < Numeric
141
144
  # cmp.to_d(precision) -> bigdecimal
142
145
  #
143
146
  # Returns the value as a BigDecimal.
147
+ # If the imaginary part is not +0+, an error is raised
144
148
  #
145
- # The +precision+ parameter is required for a rational complex number.
146
- # This parameter is used to determine the number of significant digits
147
- # for the result.
149
+ # The +precision+ parameter is used to determine the number of
150
+ # significant digits for the result. When +precision+ is set to +0+,
151
+ # the number of digits to represent the float being converted is determined
152
+ # automatically.
153
+ # The default +precision+ is +0+.
148
154
  #
149
155
  # require 'bigdecimal'
150
156
  # require 'bigdecimal/util'
151
157
  #
152
158
  # Complex(0.1234567, 0).to_d(4) # => 0.1235e0
153
159
  # Complex(Rational(22, 7), 0).to_d(3) # => 0.314e1
160
+ # Complex(1, 1).to_d # raises ArgumentError
154
161
  #
155
162
  # See also Kernel.BigDecimal.
156
163
  #
157
- def to_d(*args)
164
+ def to_d(precision=0)
158
165
  BigDecimal(self) unless self.imag.zero? # to raise error
159
166
 
160
- if args.length == 0
161
- case self.real
162
- when Rational
163
- BigDecimal(self.real) # to raise error
164
- end
165
- end
166
- self.real.to_d(*args)
167
+ BigDecimal(self.real, precision)
167
168
  end
168
169
  end
169
170
 
data/lib/bigdecimal.rb CHANGED
@@ -1,5 +1,345 @@
1
1
  if RUBY_ENGINE == 'jruby'
2
2
  JRuby::Util.load_ext("org.jruby.ext.bigdecimal.BigDecimalLibrary")
3
+
4
+ class BigDecimal
5
+ def _decimal_shift(i) # :nodoc:
6
+ to_java.move_point_right(i).to_d
7
+ end
8
+ end
3
9
  else
4
10
  require 'bigdecimal.so'
5
11
  end
12
+
13
+ class BigDecimal
14
+ module Internal # :nodoc:
15
+
16
+ # Coerce x to BigDecimal with the specified precision.
17
+ # TODO: some methods (example: BigMath.exp) require more precision than specified to coerce.
18
+ def self.coerce_to_bigdecimal(x, prec, method_name) # :nodoc:
19
+ case x
20
+ when BigDecimal
21
+ return x
22
+ when Integer, Float
23
+ return BigDecimal(x, 0)
24
+ when Rational
25
+ return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max)
26
+ end
27
+ raise ArgumentError, "#{x.inspect} can't be coerced into BigDecimal"
28
+ end
29
+
30
+ def self.validate_prec(prec, method_name, accept_zero: false) # :nodoc:
31
+ raise ArgumentError, 'precision must be an Integer' unless Integer === prec
32
+ if accept_zero
33
+ raise ArgumentError, "Negative precision for #{method_name}" if prec < 0
34
+ else
35
+ raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0
36
+ end
37
+ end
38
+
39
+ def self.infinity_computation_result # :nodoc:
40
+ if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_INFINITY)
41
+ raise FloatDomainError, "Computation results in 'Infinity'"
42
+ end
43
+ BigDecimal::INFINITY
44
+ end
45
+
46
+ def self.nan_computation_result # :nodoc:
47
+ if BigDecimal.mode(BigDecimal::EXCEPTION_ALL).anybits?(BigDecimal::EXCEPTION_NaN)
48
+ raise FloatDomainError, "Computation results to 'NaN'"
49
+ end
50
+ BigDecimal::NAN
51
+ end
52
+ end
53
+
54
+ # call-seq:
55
+ # self ** other -> bigdecimal
56
+ #
57
+ # Returns the \BigDecimal value of +self+ raised to power +other+:
58
+ #
59
+ # b = BigDecimal('3.14')
60
+ # b ** 2 # => 0.98596e1
61
+ # b ** 2.0 # => 0.98596e1
62
+ # b ** Rational(2, 1) # => 0.98596e1
63
+ #
64
+ # Related: BigDecimal#power.
65
+ #
66
+ def **(y)
67
+ case y
68
+ when BigDecimal, Integer, Float, Rational
69
+ power(y)
70
+ when nil
71
+ raise TypeError, 'wrong argument type NilClass'
72
+ else
73
+ x, y = y.coerce(self)
74
+ x**y
75
+ end
76
+ end
77
+
78
+ # call-seq:
79
+ # power(n)
80
+ # power(n, prec)
81
+ #
82
+ # Returns the value raised to the power of n.
83
+ #
84
+ # Also available as the operator **.
85
+ #
86
+ def power(y, prec = nil)
87
+ Internal.validate_prec(prec, :power) if prec
88
+ x = self
89
+ y = Internal.coerce_to_bigdecimal(y, prec || n_significant_digits, :power)
90
+
91
+ return Internal.nan_computation_result if x.nan? || y.nan?
92
+ return BigDecimal(1) if y.zero?
93
+
94
+ if y.infinite?
95
+ if x < 0
96
+ return BigDecimal(0) if x < -1 && y.negative?
97
+ return BigDecimal(0) if x > -1 && y.positive?
98
+ raise Math::DomainError, 'Result undefined for negative base raised to infinite power'
99
+ elsif x < 1
100
+ return y.positive? ? BigDecimal(0) : BigDecimal::Internal.infinity_computation_result
101
+ elsif x == 1
102
+ return BigDecimal(1)
103
+ else
104
+ return y.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0)
105
+ end
106
+ end
107
+
108
+ if x.infinite? && y < 0
109
+ # Computation result will be +0 or -0. Avoid overflow.
110
+ neg = x < 0 && y.frac.zero? && y % 2 == 1
111
+ return neg ? -BigDecimal(0) : BigDecimal(0)
112
+ end
113
+
114
+ if x.zero?
115
+ return BigDecimal(1) if y.zero?
116
+ return BigDecimal(0) if y > 0
117
+ if y.frac.zero? && y % 2 == 1 && x.sign == -1
118
+ return -BigDecimal::Internal.infinity_computation_result
119
+ else
120
+ return BigDecimal::Internal.infinity_computation_result
121
+ end
122
+ elsif x < 0
123
+ if y.frac.zero?
124
+ if y % 2 == 0
125
+ return (-x).power(y, prec)
126
+ else
127
+ return -(-x).power(y, prec)
128
+ end
129
+ else
130
+ raise Math::DomainError, 'Computation results in complex number'
131
+ end
132
+ elsif x == 1
133
+ return BigDecimal(1)
134
+ end
135
+
136
+ prec ||= BigDecimal.limit.nonzero?
137
+ frac_part = y.frac
138
+
139
+ if frac_part.zero? && !prec
140
+ # Infinite precision calculation for `x ** int` and `x.power(int)`
141
+ int_part = y.fix.to_i
142
+ int_part = -int_part if (neg = int_part < 0)
143
+ ans = BigDecimal(1)
144
+ n = 1
145
+ xn = x
146
+ while true
147
+ ans *= xn if int_part.allbits?(n)
148
+ n <<= 1
149
+ break if n > int_part
150
+ xn *= xn
151
+ # Detect overflow/underflow before consuming infinite memory
152
+ if (xn.exponent.abs - 1) * int_part / n >= 0x7FFFFFFFFFFFFFFF
153
+ return ((xn.exponent > 0) ^ neg ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0)) * (int_part.even? || x > 0 ? 1 : -1)
154
+ end
155
+ end
156
+ return neg ? BigDecimal(1) / ans : ans
157
+ end
158
+
159
+ prec ||= [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig
160
+
161
+ if y < 0
162
+ inv = x.power(-y, prec)
163
+ return BigDecimal(0) if inv.infinite?
164
+ return BigDecimal::Internal.infinity_computation_result if inv.zero?
165
+ return BigDecimal(1).div(inv, prec)
166
+ end
167
+
168
+ prec2 = prec + BigDecimal.double_fig
169
+
170
+ if frac_part.zero? && y.exponent < Math.log(prec) * 5 + 20
171
+ # Use exponentiation by squaring if y is an integer and not too large
172
+ pow_prec = prec2 + y.exponent
173
+ n = 1
174
+ xn = x
175
+ ans = BigDecimal(1)
176
+ int_part = y.fix.to_i
177
+ while true
178
+ ans = ans.mult(xn, pow_prec) if int_part.allbits?(n)
179
+ n <<= 1
180
+ break if n > int_part
181
+ xn = xn.mult(xn, pow_prec)
182
+ end
183
+ ans.mult(1, prec)
184
+ else
185
+ if x > 1
186
+ # To calculate exp(z, prec), z needs prec+max(z.exponent, 0) precision if z > 0.
187
+ # Estimate (y*log(x)).exponent
188
+ logx_exponent = x < 2 ? (x - 1).exponent : Math.log10(x.exponent).round
189
+ ylogx_exponent = y.exponent + logx_exponent
190
+ prec2 += [ylogx_exponent, 0].max
191
+ end
192
+ BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), prec)
193
+ end
194
+ end
195
+
196
+ # Returns the square root of the value.
197
+ #
198
+ # Result has at least prec significant digits.
199
+ #
200
+ def sqrt(prec)
201
+ Internal.validate_prec(prec, :sqrt, accept_zero: true)
202
+ return Internal.infinity_computation_result if infinite? == 1
203
+
204
+ raise FloatDomainError, 'sqrt of negative value' if self < 0
205
+ raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan?
206
+ return self if zero?
207
+
208
+ if prec == 0
209
+ prec = BigDecimal.limit.nonzero? || n_significant_digits + BigDecimal.double_fig
210
+ end
211
+
212
+ ex = exponent / 2
213
+ x = _decimal_shift(-2 * ex)
214
+ y = BigDecimal(Math.sqrt(x.to_f), 0)
215
+ precs = [prec + BigDecimal.double_fig]
216
+ precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
217
+ precs.reverse_each do |p|
218
+ y = y.add(x.div(y, p), p).div(2, p)
219
+ end
220
+ y._decimal_shift(ex).mult(1, prec)
221
+ end
222
+ end
223
+
224
+ # Core BigMath methods for BigDecimal (log, exp) are defined here.
225
+ # Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'.
226
+ module BigMath
227
+
228
+ # call-seq:
229
+ # BigMath.log(decimal, numeric) -> BigDecimal
230
+ #
231
+ # Computes the natural logarithm of +decimal+ to the specified number of
232
+ # digits of precision, +numeric+.
233
+ #
234
+ # If +decimal+ is zero or negative, raises Math::DomainError.
235
+ #
236
+ # If +decimal+ is positive infinity, returns Infinity.
237
+ #
238
+ # If +decimal+ is NaN, returns NaN.
239
+ #
240
+ def self.log(x, prec)
241
+ BigDecimal::Internal.validate_prec(prec, :log)
242
+ raise Math::DomainError, 'Complex argument for BigMath.log' if Complex === x
243
+
244
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log)
245
+ return BigDecimal::Internal.nan_computation_result if x.nan?
246
+ raise Math::DomainError, 'Negative argument for log' if x < 0
247
+ return -BigDecimal::Internal.infinity_computation_result if x.zero?
248
+ return BigDecimal::Internal.infinity_computation_result if x.infinite?
249
+ return BigDecimal(0) if x == 1
250
+
251
+ prec2 = prec + BigDecimal.double_fig
252
+ BigDecimal.save_limit do
253
+ BigDecimal.limit(0)
254
+ if x > 10 || x < 0.1
255
+ log10 = log(BigDecimal(10), prec2)
256
+ exponent = x.exponent
257
+ x = x._decimal_shift(-exponent)
258
+ if x < 0.3
259
+ x *= 10
260
+ exponent -= 1
261
+ end
262
+ return (log10 * exponent).add(log(x, prec2), prec)
263
+ end
264
+
265
+ x_minus_one_exponent = (x - 1).exponent
266
+
267
+ # log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps
268
+ sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max
269
+
270
+ lg2 = 0.3010299956639812
271
+ sqrt_prec = prec2 + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil
272
+
273
+ sqrt_steps.times do
274
+ x = x.sqrt(sqrt_prec)
275
+ end
276
+
277
+ # Taylor series for log(x) around 1
278
+ # log(x) = -log((1 + X) / (1 - X)) where X = (x - 1) / (x + 1)
279
+ # log(x) = 2 * (X + X**3 / 3 + X**5 / 5 + X**7 / 7 + ...)
280
+ x = (x - 1).div(x + 1, sqrt_prec)
281
+ y = x
282
+ x2 = x.mult(x, prec2)
283
+ 1.step do |i|
284
+ n = prec2 + x.exponent - y.exponent + x2.exponent
285
+ break if n <= 0 || x.zero?
286
+ x = x.mult(x2.round(n - x2.exponent), n)
287
+ y = y.add(x.div(2 * i + 1, n), prec2)
288
+ end
289
+
290
+ y.mult(2 ** (sqrt_steps + 1), prec)
291
+ end
292
+ end
293
+
294
+ # Taylor series for exp(x) around 0
295
+ private_class_method def self._exp_taylor(x, prec) # :nodoc:
296
+ xn = BigDecimal(1)
297
+ y = BigDecimal(1)
298
+ 1.step do |i|
299
+ n = prec + xn.exponent
300
+ break if n <= 0 || xn.zero?
301
+ xn = xn.mult(x, n).div(i, n)
302
+ y = y.add(xn, prec)
303
+ end
304
+ y
305
+ end
306
+
307
+ # call-seq:
308
+ # BigMath.exp(decimal, numeric) -> BigDecimal
309
+ #
310
+ # Computes the value of e (the base of natural logarithms) raised to the
311
+ # power of +decimal+, to the specified number of digits of precision.
312
+ #
313
+ # If +decimal+ is infinity, returns Infinity.
314
+ #
315
+ # If +decimal+ is NaN, returns NaN.
316
+ #
317
+ def self.exp(x, prec)
318
+ BigDecimal::Internal.validate_prec(prec, :exp)
319
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :exp)
320
+ return BigDecimal::Internal.nan_computation_result if x.nan?
321
+ return x.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) if x.infinite?
322
+ return BigDecimal(1) if x.zero?
323
+
324
+ # exp(x * 10**cnt) = exp(x)**(10**cnt)
325
+ cnt = x < -1 || x > 1 ? x.exponent : 0
326
+ prec2 = prec + BigDecimal.double_fig + cnt
327
+ x = x._decimal_shift(-cnt)
328
+
329
+ # Calculation of exp(small_prec) is fast because calculation of x**n is fast
330
+ # Calculation of exp(small_abs) converges fast.
331
+ # exp(x) = exp(small_prec_part + small_abs_part) = exp(small_prec_part) * exp(small_abs_part)
332
+ x_small_prec = x.round(Integer.sqrt(prec2))
333
+ y = _exp_taylor(x_small_prec, prec2).mult(_exp_taylor(x.sub(x_small_prec, prec2), prec2), prec2)
334
+
335
+ # calculate exp(x * 10**cnt) from exp(x)
336
+ # exp(x * 10**k) = exp(x * 10**(k - 1)) ** 10
337
+ cnt.times do
338
+ y2 = y.mult(y, prec2)
339
+ y5 = y2.mult(y2, prec2).mult(y, prec2)
340
+ y = y5.mult(y5, prec2)
341
+ end
342
+
343
+ y.mult(1, prec)
344
+ end
345
+ end
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: 3.2.2
4
+ version: 3.3.0
5
5
  platform: java
6
6
  authors:
7
7
  - Kenta Murata
@@ -9,7 +9,7 @@ authors:
9
9
  - Shigeo Kobayashi
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2025-06-04 00:00:00.000000000 Z
12
+ date: 2025-10-07 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: This library provides arbitrary-precision decimal floating-point number
15
15
  class.