bigdecimal 4.0.1-java → 4.1.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 +4 -4
- data/bigdecimal.gemspec +6 -1
- data/lib/bigdecimal/math.rb +101 -122
- data/lib/bigdecimal/util.rb +1 -1
- data/lib/bigdecimal.rb +86 -59
- data/sample/linear.rb +73 -37
- data/sample/nlsolve.rb +47 -30
- data/sample/pi.rb +2 -7
- data/sig/big_decimal.rbs +1502 -0
- data/sig/big_decimal_util.rbs +158 -0
- data/sig/big_math.rbs +423 -0
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dbec3e510145cedb03e4d1813d6e0ab22ff1327ee817ea9d2347e780fe6d5bf1
|
|
4
|
+
data.tar.gz: 9442152d34a18fd79367606134622d028468edf23d6dbe3404a9eaf239788f20
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d315846a325bdba82e62e2fe34b16a8ef01942e4485d6551e835c1739484b854593c33a7d568f10e461a27276ade554d0e80bb75666008d9502538d35867d513
|
|
7
|
+
data.tar.gz: 7f0bf10269c3c1b3135b8d4b3dff286e5a7ad9c2f13c976f333ce3191c4fbe565f9ba35c19d041b2fbaa7bb2d7ede35e17d816c1cfb7357426f6e798eff471c2
|
data/bigdecimal.gemspec
CHANGED
|
@@ -34,6 +34,9 @@ Gem::Specification.new do |s|
|
|
|
34
34
|
sample/linear.rb
|
|
35
35
|
sample/nlsolve.rb
|
|
36
36
|
sample/pi.rb
|
|
37
|
+
sig/big_decimal_util.rbs
|
|
38
|
+
sig/big_decimal.rbs
|
|
39
|
+
sig/big_math.rbs
|
|
37
40
|
]
|
|
38
41
|
if Gem::Platform === s.platform and s.platform =~ 'java' or RUBY_ENGINE == 'jruby'
|
|
39
42
|
s.platform = 'java'
|
|
@@ -43,15 +46,17 @@ Gem::Specification.new do |s|
|
|
|
43
46
|
ext/bigdecimal/bigdecimal.c
|
|
44
47
|
ext/bigdecimal/bigdecimal.h
|
|
45
48
|
ext/bigdecimal/bits.h
|
|
49
|
+
ext/bigdecimal/div.h
|
|
46
50
|
ext/bigdecimal/feature.h
|
|
47
51
|
ext/bigdecimal/missing.c
|
|
48
52
|
ext/bigdecimal/missing.h
|
|
53
|
+
ext/bigdecimal/ntt.h
|
|
49
54
|
ext/bigdecimal/missing/dtoa.c
|
|
50
55
|
ext/bigdecimal/static_assert.h
|
|
51
56
|
]
|
|
52
57
|
end
|
|
53
58
|
|
|
54
|
-
s.required_ruby_version = Gem::Requirement.new(">= 2.
|
|
59
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
|
|
55
60
|
|
|
56
61
|
s.metadata["changelog_uri"] = s.homepage + "/blob/master/CHANGES.md"
|
|
57
62
|
end
|
data/lib/bigdecimal/math.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# frozen_string_literal:
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
require 'bigdecimal'
|
|
3
3
|
|
|
4
4
|
#
|
|
@@ -75,8 +75,8 @@ module BigMath
|
|
|
75
75
|
private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc:
|
|
76
76
|
return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi
|
|
77
77
|
|
|
78
|
-
mod_prec = prec + BigDecimal
|
|
79
|
-
pi_extra_prec = [x.exponent, 0].max + BigDecimal
|
|
78
|
+
mod_prec = prec + BigDecimal::Internal::EXTRA_PREC
|
|
79
|
+
pi_extra_prec = [x.exponent, 0].max + BigDecimal::Internal::EXTRA_PREC
|
|
80
80
|
while true
|
|
81
81
|
pi = PI(mod_prec + pi_extra_prec)
|
|
82
82
|
half_pi = pi / 2
|
|
@@ -84,16 +84,47 @@ module BigMath
|
|
|
84
84
|
mod -= half_pi
|
|
85
85
|
if mod.zero? || mod_prec + mod.exponent <= 0
|
|
86
86
|
# mod is too small to estimate required pi precision
|
|
87
|
-
mod_prec = mod_prec * 3 / 2 + BigDecimal
|
|
87
|
+
mod_prec = mod_prec * 3 / 2 + BigDecimal::Internal::EXTRA_PREC
|
|
88
88
|
elsif mod_prec + mod.exponent < prec
|
|
89
89
|
# Estimate required precision of pi
|
|
90
|
-
mod_prec = prec - mod.exponent + BigDecimal
|
|
90
|
+
mod_prec = prec - mod.exponent + BigDecimal::Internal::EXTRA_PREC
|
|
91
91
|
else
|
|
92
92
|
return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
+
private_class_method def _sin_binary_splitting(x, prec) # :nodoc:
|
|
98
|
+
return x if x.zero?
|
|
99
|
+
x2 = x.mult(x, prec)
|
|
100
|
+
# Find k that satisfies x2**k / (2k+1)! < 10**(-prec)
|
|
101
|
+
log10 = Math.log(10)
|
|
102
|
+
logx = BigDecimal::Internal.float_log(x.abs)
|
|
103
|
+
step = (1..).bsearch { |k| Math.lgamma(2 * k + 1)[0] - 2 * k * logx > prec * log10 }
|
|
104
|
+
# Construct denominator sequence for binary splitting
|
|
105
|
+
# sin(x) = x*(1-x2/(2*3)*(1-x2/(4*5)*(1-x2/(6*7)*(1-x2/(8*9)*(1-...)))))
|
|
106
|
+
ds = (1..step).map {|i| -(2 * i) * (2 * i + 1) }
|
|
107
|
+
x.mult(1 + BigDecimal::Internal.taylor_sum_binary_splitting(x2, ds, prec), prec)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private_class_method def _sin_around_zero(x, prec) # :nodoc:
|
|
111
|
+
# Divide x into several parts
|
|
112
|
+
# sin(x.xxxxxxxx...) = sin(x.xx + 0.00xx + 0.0000xxxx + ...)
|
|
113
|
+
# Calculate sin of each part and restore sin(0.xxxxxxxx...) using addition theorem.
|
|
114
|
+
sin = BigDecimal(0)
|
|
115
|
+
cos = BigDecimal(1)
|
|
116
|
+
n = 2
|
|
117
|
+
while x != 0 do
|
|
118
|
+
partial_x = x.truncate(n)
|
|
119
|
+
x -= partial_x
|
|
120
|
+
s = _sin_binary_splitting(partial_x, prec)
|
|
121
|
+
c = (1 - s * s).sqrt(prec)
|
|
122
|
+
sin, cos = (sin * c).add(cos * s, prec), (cos * c).sub(sin * s, prec)
|
|
123
|
+
n *= 2
|
|
124
|
+
end
|
|
125
|
+
sin.clamp(BigDecimal(-1), BigDecimal(1))
|
|
126
|
+
end
|
|
127
|
+
|
|
97
128
|
# call-seq:
|
|
98
129
|
# cbrt(decimal, numeric) -> BigDecimal
|
|
99
130
|
#
|
|
@@ -114,9 +145,7 @@ module BigMath
|
|
|
114
145
|
ex = x.exponent / 3
|
|
115
146
|
x = x._decimal_shift(-3 * ex)
|
|
116
147
|
y = BigDecimal(Math.cbrt(x.to_f), 0)
|
|
117
|
-
|
|
118
|
-
precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
|
|
119
|
-
precs.reverse_each do |p|
|
|
148
|
+
BigDecimal::Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
|
|
120
149
|
y = (2 * y + x.div(y, p).div(y, p)).div(3, p)
|
|
121
150
|
end
|
|
122
151
|
y._decimal_shift(ex).mult(neg ? -1 : 1, prec)
|
|
@@ -137,7 +166,7 @@ module BigMath
|
|
|
137
166
|
y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :hypot)
|
|
138
167
|
return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan?
|
|
139
168
|
return BigDecimal::Internal.infinity_computation_result if x.infinite? || y.infinite?
|
|
140
|
-
prec2 = prec + BigDecimal
|
|
169
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
141
170
|
sqrt(x.mult(x, prec2) + y.mult(y, prec2), prec)
|
|
142
171
|
end
|
|
143
172
|
|
|
@@ -156,26 +185,9 @@ module BigMath
|
|
|
156
185
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :sin)
|
|
157
186
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
|
|
158
187
|
return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
|
|
159
|
-
n
|
|
160
|
-
one = BigDecimal("1")
|
|
161
|
-
two = BigDecimal("2")
|
|
188
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
162
189
|
sign, x = _sin_periodic_reduction(x, n)
|
|
163
|
-
|
|
164
|
-
x2 = x.mult(x,n)
|
|
165
|
-
y = x
|
|
166
|
-
d = y
|
|
167
|
-
i = one
|
|
168
|
-
z = one
|
|
169
|
-
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
|
170
|
-
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
|
171
|
-
x1 = -x2.mult(x1,n)
|
|
172
|
-
i += two
|
|
173
|
-
z *= (i-one) * i
|
|
174
|
-
d = x1.div(z,m)
|
|
175
|
-
y += d
|
|
176
|
-
end
|
|
177
|
-
y = BigDecimal("1") if y > 1
|
|
178
|
-
y.mult(sign, prec)
|
|
190
|
+
_sin_around_zero(x, n).mult(sign, prec)
|
|
179
191
|
end
|
|
180
192
|
|
|
181
193
|
# call-seq:
|
|
@@ -193,8 +205,9 @@ module BigMath
|
|
|
193
205
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :cos)
|
|
194
206
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
|
|
195
207
|
return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
|
|
196
|
-
|
|
197
|
-
sign
|
|
208
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
209
|
+
sign, x = _sin_periodic_reduction(x, n, add_half_pi: true)
|
|
210
|
+
_sin_around_zero(x, n).mult(sign, prec)
|
|
198
211
|
end
|
|
199
212
|
|
|
200
213
|
# call-seq:
|
|
@@ -213,7 +226,8 @@ module BigMath
|
|
|
213
226
|
#
|
|
214
227
|
def tan(x, prec)
|
|
215
228
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :tan)
|
|
216
|
-
|
|
229
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
230
|
+
sin(x, prec2).div(cos(x, prec2), prec)
|
|
217
231
|
end
|
|
218
232
|
|
|
219
233
|
# call-seq:
|
|
@@ -233,7 +247,7 @@ module BigMath
|
|
|
233
247
|
raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1
|
|
234
248
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
235
249
|
|
|
236
|
-
prec2 = prec + BigDecimal
|
|
250
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
237
251
|
cos = (1 - x**2).sqrt(prec2)
|
|
238
252
|
if cos.zero?
|
|
239
253
|
PI(prec2).div(x > 0 ? 2 : -2, prec)
|
|
@@ -259,7 +273,7 @@ module BigMath
|
|
|
259
273
|
raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1
|
|
260
274
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
261
275
|
|
|
262
|
-
prec2 = prec + BigDecimal
|
|
276
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
263
277
|
return (PI(prec2) / 2).sub(asin(x, prec2), prec) if x < 0
|
|
264
278
|
return PI(prec2).div(2, prec) if x.zero?
|
|
265
279
|
|
|
@@ -282,29 +296,22 @@ module BigMath
|
|
|
282
296
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan)
|
|
283
297
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
|
|
284
298
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
285
|
-
n = prec + BigDecimal
|
|
286
|
-
|
|
299
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
300
|
+
return PI(n).div(2 * x.infinite?, prec) if x.infinite?
|
|
301
|
+
|
|
287
302
|
x = -x if neg = x < 0
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
y = x
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
|
298
|
-
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
|
299
|
-
t = -t.mult(x2,n)
|
|
300
|
-
d = t.div(r,m)
|
|
301
|
-
y += d
|
|
302
|
-
r += 2
|
|
303
|
+
x = BigDecimal(1).div(x, n) if inv = x < -1 || x > 1
|
|
304
|
+
|
|
305
|
+
# Solve tan(y) - x = 0 with Newton's method
|
|
306
|
+
# Repeat: y -= (tan(y) - x) * cos(y)**2
|
|
307
|
+
y = BigDecimal(Math.atan(x.to_f), 0)
|
|
308
|
+
BigDecimal::Internal.newton_loop(n) do |p|
|
|
309
|
+
s = sin(y, p)
|
|
310
|
+
c = (1 - s * s).sqrt(p)
|
|
311
|
+
y = y.sub(c * (s.sub(c * x.mult(1, p), p)), p)
|
|
303
312
|
end
|
|
304
|
-
y
|
|
305
|
-
y
|
|
306
|
-
y = -y if neg
|
|
307
|
-
y.mult(1, prec)
|
|
313
|
+
y = PI(n) / 2 - y if inv
|
|
314
|
+
y.mult(neg ? -1 : 1, prec)
|
|
308
315
|
end
|
|
309
316
|
|
|
310
317
|
# call-seq:
|
|
@@ -333,7 +340,7 @@ module BigMath
|
|
|
333
340
|
|
|
334
341
|
y = -y if neg = y < 0
|
|
335
342
|
xlarge = y.abs < x.abs
|
|
336
|
-
prec2 = prec + BigDecimal
|
|
343
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
337
344
|
if x > 0
|
|
338
345
|
v = xlarge ? atan(y.div(x, prec2), prec) : PI(prec2) / 2 - atan(x.div(y, prec2), prec2)
|
|
339
346
|
else
|
|
@@ -359,7 +366,7 @@ module BigMath
|
|
|
359
366
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
360
367
|
return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
|
|
361
368
|
|
|
362
|
-
prec2 = prec + BigDecimal
|
|
369
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
363
370
|
prec2 -= x.exponent if x.exponent < 0
|
|
364
371
|
e = exp(x, prec2)
|
|
365
372
|
(e - BigDecimal(1).div(e, prec2)).div(2, prec)
|
|
@@ -382,7 +389,7 @@ module BigMath
|
|
|
382
389
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
383
390
|
return BigDecimal::Internal.infinity_computation_result if x.infinite?
|
|
384
391
|
|
|
385
|
-
prec2 = prec + BigDecimal
|
|
392
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
386
393
|
e = exp(x, prec2)
|
|
387
394
|
(e + BigDecimal(1).div(e, prec2)).div(2, prec)
|
|
388
395
|
end
|
|
@@ -404,7 +411,7 @@ module BigMath
|
|
|
404
411
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
405
412
|
return BigDecimal(x.infinite?) if x.infinite?
|
|
406
413
|
|
|
407
|
-
prec2 = prec + BigDecimal
|
|
414
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC + [-x.exponent, 0].max
|
|
408
415
|
e = exp(x, prec2)
|
|
409
416
|
einv = BigDecimal(1).div(e, prec2)
|
|
410
417
|
(e - einv).div(e + einv, prec)
|
|
@@ -428,7 +435,7 @@ module BigMath
|
|
|
428
435
|
return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
|
|
429
436
|
return -asinh(-x, prec) if x < 0
|
|
430
437
|
|
|
431
|
-
sqrt_prec = prec + [-x.exponent, 0].max + BigDecimal
|
|
438
|
+
sqrt_prec = prec + [-x.exponent, 0].max + BigDecimal::Internal::EXTRA_PREC
|
|
432
439
|
log(x + sqrt(x**2 + 1, sqrt_prec), prec)
|
|
433
440
|
end
|
|
434
441
|
|
|
@@ -450,7 +457,7 @@ module BigMath
|
|
|
450
457
|
return BigDecimal::Internal.infinity_computation_result if x.infinite?
|
|
451
458
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
452
459
|
|
|
453
|
-
log(x + sqrt(x**2 - 1, prec + BigDecimal
|
|
460
|
+
log(x + sqrt(x**2 - 1, prec + BigDecimal::Internal::EXTRA_PREC), prec)
|
|
454
461
|
end
|
|
455
462
|
|
|
456
463
|
# call-seq:
|
|
@@ -472,7 +479,7 @@ module BigMath
|
|
|
472
479
|
return BigDecimal::Internal.infinity_computation_result if x == 1
|
|
473
480
|
return -BigDecimal::Internal.infinity_computation_result if x == -1
|
|
474
481
|
|
|
475
|
-
prec2 = prec + BigDecimal
|
|
482
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
476
483
|
(log(x + 1, prec2) - log(1 - x, prec2)).div(2, prec)
|
|
477
484
|
end
|
|
478
485
|
|
|
@@ -497,10 +504,10 @@ module BigMath
|
|
|
497
504
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
498
505
|
return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
|
|
499
506
|
|
|
500
|
-
prec2 = prec + BigDecimal
|
|
507
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC * 3 / 2
|
|
501
508
|
v = log(x, prec2).div(log(BigDecimal(2), prec2), prec2)
|
|
502
509
|
# Perform half-up rounding to calculate log2(2**n)==n correctly in every rounding mode
|
|
503
|
-
v = v.round(prec + BigDecimal
|
|
510
|
+
v = v.round(prec + BigDecimal::Internal::EXTRA_PREC - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
|
|
504
511
|
v.mult(1, prec)
|
|
505
512
|
end
|
|
506
513
|
|
|
@@ -525,10 +532,10 @@ module BigMath
|
|
|
525
532
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
526
533
|
return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
|
|
527
534
|
|
|
528
|
-
prec2 = prec + BigDecimal
|
|
535
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC * 3 / 2
|
|
529
536
|
v = log(x, prec2).div(log(BigDecimal(10), prec2), prec2)
|
|
530
537
|
# Perform half-up rounding to calculate log10(10**n)==n correctly in every rounding mode
|
|
531
|
-
v = v.round(prec + BigDecimal
|
|
538
|
+
v = v.round(prec + BigDecimal::Internal::EXTRA_PREC - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
|
|
532
539
|
v.mult(1, prec)
|
|
533
540
|
end
|
|
534
541
|
|
|
@@ -565,25 +572,19 @@ module BigMath
|
|
|
565
572
|
if x < -1
|
|
566
573
|
# log10(exp(x)) = x * log10(e)
|
|
567
574
|
lg_e = 0.4342944819032518
|
|
568
|
-
exp_prec = prec + (lg_e * x).ceil + BigDecimal
|
|
575
|
+
exp_prec = prec + (lg_e * x).ceil + BigDecimal::Internal::EXTRA_PREC
|
|
569
576
|
elsif x < 1
|
|
570
|
-
exp_prec = prec - x.exponent + BigDecimal
|
|
577
|
+
exp_prec = prec - x.exponent + BigDecimal::Internal::EXTRA_PREC
|
|
571
578
|
else
|
|
572
579
|
exp_prec = prec
|
|
573
580
|
end
|
|
574
581
|
|
|
575
582
|
return BigDecimal(-1) if exp_prec <= 0
|
|
576
583
|
|
|
577
|
-
exp
|
|
578
|
-
|
|
579
|
-
if exp.exponent > prec + BigDecimal.double_fig
|
|
580
|
-
# Workaroudn for https://github.com/ruby/bigdecimal/issues/464
|
|
581
|
-
exp
|
|
582
|
-
else
|
|
583
|
-
exp.sub(1, prec)
|
|
584
|
-
end
|
|
584
|
+
exp(x, exp_prec).sub(1, prec)
|
|
585
585
|
end
|
|
586
586
|
|
|
587
|
+
# call-seq:
|
|
587
588
|
# erf(decimal, numeric) -> BigDecimal
|
|
588
589
|
#
|
|
589
590
|
# Computes the error function of +decimal+ to the specified number of digits of
|
|
@@ -608,15 +609,10 @@ module BigMath
|
|
|
608
609
|
log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
|
|
609
610
|
erfc_prec = [prec + log10_erfc.ceil, 1].max
|
|
610
611
|
erfc = _erfc_asymptotic(x, erfc_prec)
|
|
611
|
-
if erfc
|
|
612
|
-
# Workaround for https://github.com/ruby/bigdecimal/issues/464
|
|
613
|
-
return BigDecimal(1) if erfc.exponent < -prec - BigDecimal.double_fig
|
|
614
|
-
|
|
615
|
-
return BigDecimal(1).sub(erfc, prec)
|
|
616
|
-
end
|
|
612
|
+
return BigDecimal(1).sub(erfc, prec) if erfc
|
|
617
613
|
end
|
|
618
614
|
|
|
619
|
-
prec2 = prec + BigDecimal
|
|
615
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
620
616
|
x_smallprec = x.mult(1, Integer.sqrt(prec2) / 2)
|
|
621
617
|
# Taylor series of x with small precision is fast
|
|
622
618
|
erf1 = _erf_taylor(x_smallprec, BigDecimal(0), BigDecimal(0), prec2)
|
|
@@ -640,7 +636,7 @@ module BigMath
|
|
|
640
636
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc)
|
|
641
637
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
642
638
|
return BigDecimal(1 - x.infinite?) if x.infinite?
|
|
643
|
-
return BigDecimal(1).sub(erf(x, prec + BigDecimal
|
|
639
|
+
return BigDecimal(1).sub(erf(x, prec + BigDecimal::Internal::EXTRA_PREC), prec) if x < 0.5
|
|
644
640
|
return BigDecimal(0) if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
|
|
645
641
|
|
|
646
642
|
if x >= 8
|
|
@@ -649,9 +645,10 @@ module BigMath
|
|
|
649
645
|
end
|
|
650
646
|
|
|
651
647
|
# erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
|
|
652
|
-
# Precision of erf(x) needs about log10(exp(-x**2)) extra digits
|
|
648
|
+
# Precision of erf(x) needs about log10(exp(-x**2)/x/sqrt(pi)) extra digits
|
|
653
649
|
log10 = 2.302585092994046
|
|
654
|
-
|
|
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
|
|
655
652
|
BigDecimal(1).sub(erf(x, high_prec), prec)
|
|
656
653
|
end
|
|
657
654
|
|
|
@@ -700,7 +697,7 @@ module BigMath
|
|
|
700
697
|
# Using Stirling's approximation, we can simplify this condition to:
|
|
701
698
|
# sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
|
|
702
699
|
# and the left side is minimized when k = x**2.
|
|
703
|
-
prec += BigDecimal
|
|
700
|
+
prec += BigDecimal::Internal::EXTRA_PREC
|
|
704
701
|
xf = x.to_f
|
|
705
702
|
kmax = (1..(xf ** 2).floor).bsearch do |k|
|
|
706
703
|
Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
|
|
@@ -708,7 +705,8 @@ module BigMath
|
|
|
708
705
|
return unless kmax
|
|
709
706
|
|
|
710
707
|
sum = BigDecimal(1)
|
|
711
|
-
|
|
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)
|
|
712
710
|
d = BigDecimal(1)
|
|
713
711
|
(1..kmax).each do |k|
|
|
714
712
|
d = d.div(x2, prec).mult(1 - 2 * k, prec).div(2, prec)
|
|
@@ -729,7 +727,7 @@ module BigMath
|
|
|
729
727
|
def gamma(x, prec)
|
|
730
728
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :gamma)
|
|
731
729
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :gamma)
|
|
732
|
-
prec2 = prec + BigDecimal
|
|
730
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
733
731
|
if x < 0.5
|
|
734
732
|
raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero?
|
|
735
733
|
|
|
@@ -757,7 +755,7 @@ module BigMath
|
|
|
757
755
|
def lgamma(x, prec)
|
|
758
756
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :lgamma)
|
|
759
757
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :lgamma)
|
|
760
|
-
prec2 = prec + BigDecimal
|
|
758
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
761
759
|
if x < 0.5
|
|
762
760
|
return [BigDecimal::INFINITY, 1] if x.frac.zero?
|
|
763
761
|
|
|
@@ -766,7 +764,7 @@ module BigMath
|
|
|
766
764
|
pi = PI(prec2)
|
|
767
765
|
sin = _sinpix(x, pi, prec2)
|
|
768
766
|
log_gamma = BigMath.log(pi, prec2).sub(lgamma(1 - x, prec2).first + BigMath.log(sin.abs, prec2), prec)
|
|
769
|
-
return [log_gamma, sin > 0 ? 1 : -1] if prec2 + log_gamma.exponent > prec + BigDecimal
|
|
767
|
+
return [log_gamma, sin > 0 ? 1 : -1] if prec2 + log_gamma.exponent > prec + BigDecimal::Internal::EXTRA_PREC
|
|
770
768
|
|
|
771
769
|
# Retry with higher precision if loss of significance is too large
|
|
772
770
|
prec2 = prec2 * 3 / 2
|
|
@@ -816,7 +814,7 @@ module BigMath
|
|
|
816
814
|
loggamma_k = 0
|
|
817
815
|
ck_exponents = (1..a-1).map do |k|
|
|
818
816
|
loggamma_k += Math.log10(k - 1) if k > 1
|
|
819
|
-
-loggamma_k - k / log10f + (k - 0.5) * Math.log10(a - k) -
|
|
817
|
+
-loggamma_k - k / log10f + (k - 0.5) * Math.log10(a - k) - BigDecimal::Internal.float_log(x_low_prec.add(k, low_prec)) / log10f
|
|
820
818
|
end
|
|
821
819
|
|
|
822
820
|
# Estimate exponent of sum by Stirling's approximation
|
|
@@ -897,39 +895,20 @@ module BigMath
|
|
|
897
895
|
# #=> "0.31415926535897932384626433832795e1"
|
|
898
896
|
#
|
|
899
897
|
def PI(prec)
|
|
898
|
+
# Gauss–Legendre algorithm
|
|
900
899
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :PI)
|
|
901
|
-
n
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
d = one
|
|
912
|
-
k = one
|
|
913
|
-
t = BigDecimal("-80")
|
|
914
|
-
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
|
|
915
|
-
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
|
916
|
-
t = t*m25
|
|
917
|
-
d = t.div(k,m)
|
|
918
|
-
k = k+two
|
|
919
|
-
pi = pi + d
|
|
920
|
-
end
|
|
921
|
-
|
|
922
|
-
d = one
|
|
923
|
-
k = one
|
|
924
|
-
t = BigDecimal("956")
|
|
925
|
-
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
|
|
926
|
-
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
|
927
|
-
t = t.div(m57121,n)
|
|
928
|
-
d = t.div(k,m)
|
|
929
|
-
pi = pi + d
|
|
930
|
-
k = k+two
|
|
900
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
901
|
+
a = BigDecimal(1)
|
|
902
|
+
b = BigDecimal(0.5, 0).sqrt(n)
|
|
903
|
+
s = BigDecimal(0.25, 0)
|
|
904
|
+
t = 1
|
|
905
|
+
while a != b && (a - b).exponent > 1 - n
|
|
906
|
+
c = (a - b).div(2, n)
|
|
907
|
+
a, b = (a + b).div(2, n), (a * b).sqrt(n)
|
|
908
|
+
s = s.sub(c * c * t, n)
|
|
909
|
+
t *= 2
|
|
931
910
|
end
|
|
932
|
-
|
|
911
|
+
(a * b).div(s, prec)
|
|
933
912
|
end
|
|
934
913
|
|
|
935
914
|
# call-seq:
|
data/lib/bigdecimal/util.rb
CHANGED