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.
- checksums.yaml +4 -4
- data/ext/bigdecimal/bigdecimal.c +40 -47
- data/lib/bigdecimal/jacobian.rb +2 -0
- data/lib/bigdecimal/ludcmp.rb +2 -0
- data/lib/bigdecimal/math.rb +785 -71
- data/lib/bigdecimal/newton.rb +2 -0
- data/lib/bigdecimal/util.rb +15 -14
- data/lib/bigdecimal.rb +98 -67
- metadata +2 -2
data/lib/bigdecimal/newton.rb
CHANGED
data/lib/bigdecimal/util.rb
CHANGED
|
@@ -119,8 +119,11 @@ class Rational < Numeric
|
|
|
119
119
|
#
|
|
120
120
|
# Returns the value as a BigDecimal.
|
|
121
121
|
#
|
|
122
|
-
# The
|
|
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
|
|
146
|
-
#
|
|
147
|
-
#
|
|
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(
|
|
164
|
+
def to_d(precision=0)
|
|
158
165
|
BigDecimal(self) unless self.imag.zero? # to raise error
|
|
159
166
|
|
|
160
|
-
|
|
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
|
-
|
|
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.
|
|
26
|
-
|
|
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 =
|
|
82
|
-
Internal.
|
|
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
|
-
|
|
147
|
+
limit = BigDecimal.limit
|
|
132
148
|
frac_part = y.frac
|
|
133
149
|
|
|
134
|
-
if frac_part.zero? &&
|
|
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
|
|
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,
|
|
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,
|
|
179
|
+
return BigDecimal(1).div(inv, result_prec)
|
|
161
180
|
end
|
|
162
181
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
ans
|
|
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.
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
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
|
|
230
|
-
BigDecimal::Internal.
|
|
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, '
|
|
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),
|
|
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
|
|
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(
|
|
283
|
+
sqrt_steps = [Integer.sqrt(prec2) + 3 * x_minus_one_exponent, 0].max
|
|
257
284
|
|
|
258
285
|
lg2 = 0.3010299956639812
|
|
259
|
-
|
|
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(
|
|
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,
|
|
295
|
+
x = (x - 1).div(x + 1, sqrt_prec)
|
|
272
296
|
y = x
|
|
273
|
-
x2 = x.mult(x,
|
|
297
|
+
x2 = x.mult(x, prec2)
|
|
274
298
|
1.step do |i|
|
|
275
|
-
n =
|
|
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),
|
|
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
|
|
296
|
-
BigDecimal::Internal.
|
|
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
|
-
#
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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:
|
|
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.
|
|
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: []
|