bigdecimal 3.2.3 → 4.0.1

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.
@@ -2,6 +2,8 @@
2
2
  require "bigdecimal/ludcmp"
3
3
  require "bigdecimal/jacobian"
4
4
 
5
+ warn "'bigdecimal/newton' is deprecated and will be removed in a future release."
6
+
5
7
  #
6
8
  # newton.rb
7
9
  #
@@ -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,6 +1,11 @@
1
1
  if RUBY_ENGINE == 'jruby'
2
2
  JRuby::Util.load_ext("org.jruby.ext.bigdecimal.BigDecimalLibrary")
3
- return
3
+
4
+ class BigDecimal
5
+ def _decimal_shift(i) # :nodoc:
6
+ to_java.move_point_right(i).to_d
7
+ end
8
+ end
4
9
  else
5
10
  require 'bigdecimal.so'
6
11
  end
@@ -15,20 +20,31 @@ class BigDecimal
15
20
  when BigDecimal
16
21
  return x
17
22
  when Integer, Float
18
- return BigDecimal(x)
23
+ return BigDecimal(x, 0)
19
24
  when Rational
20
25
  return BigDecimal(x, [prec, 2 * BigDecimal.double_fig].max)
21
26
  end
22
27
  raise ArgumentError, "#{x.inspect} can't be coerced into BigDecimal"
23
28
  end
24
29
 
25
- def self.validate_prec(prec, method_name, accept_zero: false) # :nodoc:
26
- raise ArgumentError, 'precision must be an Integer' unless Integer === prec
30
+ def self.coerce_validate_prec(prec, method_name, accept_zero: false) # :nodoc:
31
+ unless Integer === prec
32
+ original = prec
33
+ # Emulate Integer.try_convert for ruby < 3.1
34
+ if prec.respond_to?(:to_int)
35
+ prec = prec.to_int
36
+ else
37
+ raise TypeError, "no implicit conversion of #{original.class} into Integer"
38
+ end
39
+ raise TypeError, "can't convert #{original.class} to Integer" unless Integer === prec
40
+ end
41
+
27
42
  if accept_zero
28
43
  raise ArgumentError, "Negative precision for #{method_name}" if prec < 0
29
44
  else
30
45
  raise ArgumentError, "Zero or negative precision for #{method_name}" if prec <= 0
31
46
  end
47
+ prec
32
48
  end
33
49
 
34
50
  def self.infinity_computation_result # :nodoc:
@@ -78,10 +94,10 @@ class BigDecimal
78
94
  #
79
95
  # Also available as the operator **.
80
96
  #
81
- def power(y, prec = nil)
82
- Internal.validate_prec(prec, :power) if prec
97
+ def power(y, prec = 0)
98
+ prec = Internal.coerce_validate_prec(prec, :power, accept_zero: true)
83
99
  x = self
84
- y = Internal.coerce_to_bigdecimal(y, prec || n_significant_digits, :power)
100
+ y = Internal.coerce_to_bigdecimal(y, prec.nonzero? || n_significant_digits, :power)
85
101
 
86
102
  return Internal.nan_computation_result if x.nan? || y.nan?
87
103
  return BigDecimal(1) if y.zero?
@@ -128,10 +144,10 @@ class BigDecimal
128
144
  return BigDecimal(1)
129
145
  end
130
146
 
131
- prec ||= BigDecimal.limit.nonzero?
147
+ limit = BigDecimal.limit
132
148
  frac_part = y.frac
133
149
 
134
- if frac_part.zero? && !prec
150
+ if frac_part.zero? && prec.zero? && limit.zero?
135
151
  # Infinite precision calculation for `x ** int` and `x.power(int)`
136
152
  int_part = y.fix.to_i
137
153
  int_part = -int_part if (neg = int_part < 0)
@@ -151,31 +167,42 @@ class BigDecimal
151
167
  return neg ? BigDecimal(1) / ans : ans
152
168
  end
153
169
 
154
- prec ||= [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig
170
+ result_prec = prec.nonzero? || [x.n_significant_digits, y.n_significant_digits, BigDecimal.double_fig].max + BigDecimal.double_fig
171
+ result_prec = [result_prec, limit].min if prec.zero? && limit.nonzero?
172
+
173
+ prec2 = result_prec + BigDecimal.double_fig
155
174
 
156
175
  if y < 0
157
- inv = x.power(-y, prec)
176
+ inv = x.power(-y, prec2)
158
177
  return BigDecimal(0) if inv.infinite?
159
178
  return BigDecimal::Internal.infinity_computation_result if inv.zero?
160
- return BigDecimal(1).div(inv, prec)
179
+ return BigDecimal(1).div(inv, result_prec)
161
180
  end
162
181
 
163
- int_part = y.fix.to_i
164
- prec2 = prec + BigDecimal.double_fig
165
- pow_prec = prec2 + (int_part > 0 ? y.exponent : 0)
166
- ans = BigDecimal(1)
167
- n = 1
168
- xn = x
169
- while true
170
- ans = ans.mult(xn, pow_prec) if int_part.allbits?(n)
171
- n <<= 1
172
- break if n > int_part
173
- xn = xn.mult(xn, pow_prec)
174
- end
175
- unless frac_part.zero?
176
- ans = ans.mult(BigMath.exp(BigMath.log(x, prec2).mult(frac_part, prec2), prec2), prec2)
182
+ if frac_part.zero? && y.exponent < Math.log(result_prec) * 5 + 20
183
+ # Use exponentiation by squaring if y is an integer and not too large
184
+ pow_prec = prec2 + y.exponent
185
+ n = 1
186
+ xn = x
187
+ ans = BigDecimal(1)
188
+ int_part = y.fix.to_i
189
+ while true
190
+ ans = ans.mult(xn, pow_prec) if int_part.allbits?(n)
191
+ n <<= 1
192
+ break if n > int_part
193
+ xn = xn.mult(xn, pow_prec)
194
+ end
195
+ ans.mult(1, result_prec)
196
+ else
197
+ if x > 1 && x.finite?
198
+ # To calculate exp(z, prec), z needs prec+max(z.exponent, 0) precision if z > 0.
199
+ # Estimate (y*log(x)).exponent
200
+ logx_exponent = x < 2 ? (x - 1).exponent : Math.log10(x.exponent).round
201
+ ylogx_exponent = y.exponent + logx_exponent
202
+ prec2 += [ylogx_exponent, 0].max
203
+ end
204
+ BigMath.exp(BigMath.log(x, prec2).mult(y, prec2), result_prec)
177
205
  end
178
- ans.mult(1, prec)
179
206
  end
180
207
 
181
208
  # Returns the square root of the value.
@@ -183,36 +210,35 @@ class BigDecimal
183
210
  # Result has at least prec significant digits.
184
211
  #
185
212
  def sqrt(prec)
186
- Internal.validate_prec(prec, :sqrt, accept_zero: true)
213
+ prec = Internal.coerce_validate_prec(prec, :sqrt, accept_zero: true)
187
214
  return Internal.infinity_computation_result if infinite? == 1
188
215
 
189
216
  raise FloatDomainError, 'sqrt of negative value' if self < 0
190
217
  raise FloatDomainError, "sqrt of 'NaN'(Not a Number)" if nan?
191
218
  return self if zero?
192
219
 
193
- limit = BigDecimal.limit.nonzero? if prec == 0
194
-
195
- # BigDecimal#sqrt calculates at least n_significant_digits precision.
196
- # This feature maybe problematic for some cases.
197
- n_digits = n_significant_digits
198
- prec = [prec, n_digits].max
220
+ if prec == 0
221
+ limit = BigDecimal.limit
222
+ prec = n_significant_digits + BigDecimal.double_fig
223
+ prec = [limit, prec].min if limit.nonzero?
224
+ end
199
225
 
200
226
  ex = exponent / 2
201
227
  x = _decimal_shift(-2 * ex)
202
- y = BigDecimal(Math.sqrt(x.to_f))
228
+ y = BigDecimal(Math.sqrt(x.to_f), 0)
203
229
  precs = [prec + BigDecimal.double_fig]
204
230
  precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
205
231
  precs.reverse_each do |p|
206
232
  y = y.add(x.div(y, p), p).div(2, p)
207
233
  end
208
- y = y.mult(1, limit) if limit
209
- y._decimal_shift(ex)
234
+ y._decimal_shift(ex).mult(1, prec)
210
235
  end
211
236
  end
212
237
 
213
238
  # Core BigMath methods for BigDecimal (log, exp) are defined here.
214
239
  # Other methods (sin, cos, atan) are defined in 'bigdecimal/math.rb'.
215
240
  module BigMath
241
+ module_function
216
242
 
217
243
  # call-seq:
218
244
  # BigMath.log(decimal, numeric) -> BigDecimal
@@ -226,62 +252,73 @@ module BigMath
226
252
  #
227
253
  # If +decimal+ is NaN, returns NaN.
228
254
  #
229
- def self.log(x, prec)
230
- BigDecimal::Internal.validate_prec(prec, :log)
255
+ def log(x, prec)
256
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :log)
231
257
  raise Math::DomainError, 'Complex argument for BigMath.log' if Complex === x
232
258
 
233
259
  x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log)
234
260
  return BigDecimal::Internal.nan_computation_result if x.nan?
235
- raise Math::DomainError, 'Zero or negative argument for log' if x <= 0
261
+ raise Math::DomainError, 'Negative argument for log' if x < 0
262
+ return -BigDecimal::Internal.infinity_computation_result if x.zero?
236
263
  return BigDecimal::Internal.infinity_computation_result if x.infinite?
237
264
  return BigDecimal(0) if x == 1
238
265
 
266
+ prec2 = prec + BigDecimal.double_fig
239
267
  BigDecimal.save_limit do
240
268
  BigDecimal.limit(0)
241
269
  if x > 10 || x < 0.1
242
- log10 = log(BigDecimal(10), prec)
270
+ log10 = log(BigDecimal(10), prec2)
243
271
  exponent = x.exponent
244
272
  x = x._decimal_shift(-exponent)
245
273
  if x < 0.3
246
274
  x *= 10
247
275
  exponent -= 1
248
276
  end
249
- return log10 * exponent + log(x, prec)
277
+ return (log10 * exponent).add(log(x, prec2), prec)
250
278
  end
251
279
 
252
280
  x_minus_one_exponent = (x - 1).exponent
253
- prec += BigDecimal.double_fig
254
281
 
255
282
  # log(x) = log(sqrt(sqrt(sqrt(sqrt(x))))) * 2**sqrt_steps
256
- sqrt_steps = [Integer.sqrt(prec) + 3 * x_minus_one_exponent, 0].max
283
+ sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max
257
284
 
258
285
  lg2 = 0.3010299956639812
259
- prec2 = prec + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil
286
+ sqrt_prec = prec2 + [-x_minus_one_exponent, 0].max + (sqrt_steps * lg2).ceil
260
287
 
261
288
  sqrt_steps.times do
262
- x = x.sqrt(prec2)
263
-
264
- # Workaround for https://github.com/ruby/bigdecimal/issues/354
265
- x = x.mult(1, prec2 + BigDecimal.double_fig)
289
+ x = x.sqrt(sqrt_prec)
266
290
  end
267
291
 
268
292
  # Taylor series for log(x) around 1
269
293
  # log(x) = -log((1 + X) / (1 - X)) where X = (x - 1) / (x + 1)
270
294
  # log(x) = 2 * (X + X**3 / 3 + X**5 / 5 + X**7 / 7 + ...)
271
- x = (x - 1).div(x + 1, prec2)
295
+ x = (x - 1).div(x + 1, sqrt_prec)
272
296
  y = x
273
- x2 = x.mult(x, prec)
297
+ x2 = x.mult(x, prec2)
274
298
  1.step do |i|
275
- n = prec + x.exponent - y.exponent + x2.exponent
299
+ n = prec2 + x.exponent - y.exponent + x2.exponent
276
300
  break if n <= 0 || x.zero?
277
301
  x = x.mult(x2.round(n - x2.exponent), n)
278
- y = y.add(x.div(2 * i + 1, n), prec)
302
+ y = y.add(x.div(2 * i + 1, n), prec2)
279
303
  end
280
304
 
281
305
  y.mult(2 ** (sqrt_steps + 1), prec)
282
306
  end
283
307
  end
284
308
 
309
+ # Taylor series for exp(x) around 0
310
+ private_class_method def _exp_taylor(x, prec) # :nodoc:
311
+ xn = BigDecimal(1)
312
+ y = BigDecimal(1)
313
+ 1.step do |i|
314
+ n = prec + xn.exponent
315
+ break if n <= 0 || xn.zero?
316
+ xn = xn.mult(x, n).div(i, n)
317
+ y = y.add(xn, prec)
318
+ end
319
+ y
320
+ end
321
+
285
322
  # call-seq:
286
323
  # BigMath.exp(decimal, numeric) -> BigDecimal
287
324
  #
@@ -292,29 +329,23 @@ module BigMath
292
329
  #
293
330
  # If +decimal+ is NaN, returns NaN.
294
331
  #
295
- def self.exp(x, prec)
296
- BigDecimal::Internal.validate_prec(prec, :exp)
332
+ def exp(x, prec)
333
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :exp)
297
334
  x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :exp)
298
335
  return BigDecimal::Internal.nan_computation_result if x.nan?
299
336
  return x.positive? ? BigDecimal::Internal.infinity_computation_result : BigDecimal(0) if x.infinite?
300
337
  return BigDecimal(1) if x.zero?
301
- return BigDecimal(1).div(exp(-x, prec), prec) if x < 0
302
338
 
303
339
  # exp(x * 10**cnt) = exp(x)**(10**cnt)
304
- cnt = x > 1 ? x.exponent : 0
340
+ cnt = x < -1 || x > 1 ? x.exponent : 0
305
341
  prec2 = prec + BigDecimal.double_fig + cnt
306
342
  x = x._decimal_shift(-cnt)
307
- xn = BigDecimal(1)
308
- y = BigDecimal(1)
309
343
 
310
- # Taylor series for exp(x) around 0
311
- 1.step do |i|
312
- n = prec2 + xn.exponent
313
- break if n <= 0 || xn.zero?
314
- x = x.mult(1, n)
315
- xn = xn.mult(x, n).div(i, n)
316
- y = y.add(xn, prec2)
317
- end
344
+ # Calculation of exp(small_prec) is fast because calculation of x**n is fast
345
+ # Calculation of exp(small_abs) converges fast.
346
+ # exp(x) = exp(small_prec_part + small_abs_part) = exp(small_prec_part) * exp(small_abs_part)
347
+ x_small_prec = x.round(Integer.sqrt(prec2))
348
+ y = _exp_taylor(x_small_prec, prec2).mult(_exp_taylor(x.sub(x_small_prec, prec2), prec2), prec2)
318
349
 
319
350
  # calculate exp(x * 10**cnt) from exp(x)
320
351
  # exp(x * 10**k) = exp(x * 10**(k - 1)) ** 10
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.3
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenta Murata
@@ -60,7 +60,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  requirements: []
63
- rubygems_version: 3.6.7
63
+ rubygems_version: 3.6.9
64
64
  specification_version: 4
65
65
  summary: Arbitrary-precision decimal floating-point number library.
66
66
  test_files: []