bigdecimal 3.3.1 → 4.1.2

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.
@@ -1,19 +1,40 @@
1
- # frozen_string_literal: false
1
+ # frozen_string_literal: true
2
2
  require 'bigdecimal'
3
3
 
4
4
  #
5
5
  #--
6
6
  # Contents:
7
7
  # sqrt(x, prec)
8
+ # cbrt(x, prec)
9
+ # hypot(x, y, prec)
8
10
  # sin (x, prec)
9
11
  # cos (x, prec)
10
12
  # tan (x, prec)
13
+ # asin(x, prec)
14
+ # acos(x, prec)
11
15
  # atan(x, prec)
16
+ # atan2(y, x, prec)
17
+ # sinh (x, prec)
18
+ # cosh (x, prec)
19
+ # tanh (x, prec)
20
+ # asinh(x, prec)
21
+ # acosh(x, prec)
22
+ # atanh(x, prec)
23
+ # log2 (x, prec)
24
+ # log10(x, prec)
25
+ # log1p(x, prec)
26
+ # expm1(x, prec)
27
+ # erf (x, prec)
28
+ # erfc(x, prec)
29
+ # gamma(x, prec)
30
+ # lgamma(x, prec)
31
+ # frexp(x)
32
+ # ldexp(x, exponent)
12
33
  # PI (prec)
13
34
  # E (prec) == exp(1.0,prec)
14
35
  #
15
36
  # where:
16
- # x ... BigDecimal number to be computed.
37
+ # x, y ... BigDecimal number to be computed.
17
38
  # prec ... Number of digits to be obtained.
18
39
  #++
19
40
  #
@@ -54,8 +75,8 @@ module BigMath
54
75
  private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc:
55
76
  return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi
56
77
 
57
- mod_prec = prec + BigDecimal.double_fig
58
- pi_extra_prec = [x.exponent, 0].max + BigDecimal.double_fig
78
+ mod_prec = prec + BigDecimal::Internal::EXTRA_PREC
79
+ pi_extra_prec = [x.exponent, 0].max + BigDecimal::Internal::EXTRA_PREC
59
80
  while true
60
81
  pi = PI(mod_prec + pi_extra_prec)
61
82
  half_pi = pi / 2
@@ -63,16 +84,92 @@ module BigMath
63
84
  mod -= half_pi
64
85
  if mod.zero? || mod_prec + mod.exponent <= 0
65
86
  # mod is too small to estimate required pi precision
66
- mod_prec = mod_prec * 3 / 2 + BigDecimal.double_fig
87
+ mod_prec = mod_prec * 3 / 2 + BigDecimal::Internal::EXTRA_PREC
67
88
  elsif mod_prec + mod.exponent < prec
68
89
  # Estimate required precision of pi
69
- mod_prec = prec - mod.exponent + BigDecimal.double_fig
90
+ mod_prec = prec - mod.exponent + BigDecimal::Internal::EXTRA_PREC
70
91
  else
71
92
  return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
72
93
  end
73
94
  end
74
95
  end
75
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
+
128
+ # call-seq:
129
+ # cbrt(decimal, numeric) -> BigDecimal
130
+ #
131
+ # Computes the cube root of +decimal+ to the specified number of digits of
132
+ # precision, +numeric+.
133
+ #
134
+ # BigMath.cbrt(BigDecimal('2'), 32).to_s
135
+ # #=> "0.12599210498948731647672106072782e1"
136
+ #
137
+ def cbrt(x, prec)
138
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :cbrt)
139
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cbrt)
140
+ return BigDecimal::Internal.nan_computation_result if x.nan?
141
+ return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
142
+ return BigDecimal(0) if x.zero?
143
+
144
+ x = -x if neg = x < 0
145
+ ex = x.exponent / 3
146
+ x = x._decimal_shift(-3 * ex)
147
+ y = BigDecimal(Math.cbrt(x.to_f), 0)
148
+ BigDecimal::Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
149
+ y = (2 * y + x.div(y, p).div(y, p)).div(3, p)
150
+ end
151
+ y._decimal_shift(ex).mult(neg ? -1 : 1, prec)
152
+ end
153
+
154
+ # call-seq:
155
+ # hypot(x, y, numeric) -> BigDecimal
156
+ #
157
+ # Returns sqrt(x**2 + y**2) to the specified number of digits of
158
+ # precision, +numeric+.
159
+ #
160
+ # BigMath.hypot(BigDecimal('1'), BigDecimal('2'), 32).to_s
161
+ # #=> "0.22360679774997896964091736687313e1"
162
+ #
163
+ def hypot(x, y, prec)
164
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :hypot)
165
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :hypot)
166
+ y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :hypot)
167
+ return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan?
168
+ return BigDecimal::Internal.infinity_computation_result if x.infinite? || y.infinite?
169
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
170
+ sqrt(x.mult(x, prec2) + y.mult(y, prec2), prec)
171
+ end
172
+
76
173
  # call-seq:
77
174
  # sin(decimal, numeric) -> BigDecimal
78
175
  #
@@ -88,26 +185,9 @@ module BigMath
88
185
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :sin)
89
186
  x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
90
187
  return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
91
- n = prec + BigDecimal.double_fig
92
- one = BigDecimal("1")
93
- two = BigDecimal("2")
188
+ n = prec + BigDecimal::Internal::EXTRA_PREC
94
189
  sign, x = _sin_periodic_reduction(x, n)
95
- x1 = x
96
- x2 = x.mult(x,n)
97
- y = x
98
- d = y
99
- i = one
100
- z = one
101
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
102
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
103
- x1 = -x2.mult(x1,n)
104
- i += two
105
- z *= (i-one) * i
106
- d = x1.div(z,m)
107
- y += d
108
- end
109
- y = BigDecimal("1") if y > 1
110
- y.mult(sign, prec)
190
+ _sin_around_zero(x, n).mult(sign, prec)
111
191
  end
112
192
 
113
193
  # call-seq:
@@ -125,8 +205,9 @@ module BigMath
125
205
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :cos)
126
206
  x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
127
207
  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)
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)
130
211
  end
131
212
 
132
213
  # call-seq:
@@ -145,7 +226,59 @@ module BigMath
145
226
  #
146
227
  def tan(x, prec)
147
228
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :tan)
148
- sin(x, prec + BigDecimal.double_fig).div(cos(x, prec + BigDecimal.double_fig), prec)
229
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
230
+ sin(x, prec2).div(cos(x, prec2), prec)
231
+ end
232
+
233
+ # call-seq:
234
+ # asin(decimal, numeric) -> BigDecimal
235
+ #
236
+ # Computes the arcsine of +decimal+ to the specified number of digits of
237
+ # precision, +numeric+.
238
+ #
239
+ # If +decimal+ is NaN, returns NaN.
240
+ #
241
+ # BigMath.asin(BigDecimal('0.5'), 32).to_s
242
+ # #=> "0.52359877559829887307710723054658e0"
243
+ #
244
+ def asin(x, prec)
245
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :asin)
246
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asin)
247
+ raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1
248
+ return BigDecimal::Internal.nan_computation_result if x.nan?
249
+
250
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
251
+ cos = (1 - x**2).sqrt(prec2)
252
+ if cos.zero?
253
+ PI(prec2).div(x > 0 ? 2 : -2, prec)
254
+ else
255
+ atan(x.div(cos, prec2), prec)
256
+ end
257
+ end
258
+
259
+ # call-seq:
260
+ # acos(decimal, numeric) -> BigDecimal
261
+ #
262
+ # Computes the arccosine of +decimal+ to the specified number of digits of
263
+ # precision, +numeric+.
264
+ #
265
+ # If +decimal+ is NaN, returns NaN.
266
+ #
267
+ # BigMath.acos(BigDecimal('0.5'), 32).to_s
268
+ # #=> "0.10471975511965977461542144610932e1"
269
+ #
270
+ def acos(x, prec)
271
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :acos)
272
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acos)
273
+ raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1
274
+ return BigDecimal::Internal.nan_computation_result if x.nan?
275
+
276
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
277
+ return (PI(prec2) / 2).sub(asin(x, prec2), prec) if x < 0
278
+ return PI(prec2).div(2, prec) if x.zero?
279
+
280
+ sin = (1 - x**2).sqrt(prec2)
281
+ atan(sin.div(x, prec2), prec)
149
282
  end
150
283
 
151
284
  # call-seq:
@@ -163,29 +296,593 @@ module BigMath
163
296
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan)
164
297
  x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
165
298
  return BigDecimal::Internal.nan_computation_result if x.nan?
166
- n = prec + BigDecimal.double_fig
167
- pi = PI(n)
299
+ n = prec + BigDecimal::Internal::EXTRA_PREC
300
+ return PI(n).div(2 * x.infinite?, prec) if x.infinite?
301
+
168
302
  x = -x if neg = x < 0
169
- return pi.div(neg ? -2 : 2, prec) if x.infinite?
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
173
- y = x
174
- d = y
175
- t = x
176
- r = BigDecimal("3")
177
- x2 = x.mult(x,n)
178
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
179
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
180
- t = -t.mult(x2,n)
181
- d = t.div(r,m)
182
- y += d
183
- r += 2
184
- end
185
- y *= 2 if dbl
186
- y = pi / 2 - y if inv
187
- y = -y if neg
188
- y.mult(1, prec)
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)
312
+ end
313
+ y = PI(n) / 2 - y if inv
314
+ y.mult(neg ? -1 : 1, prec)
315
+ end
316
+
317
+ # call-seq:
318
+ # atan2(decimal, decimal, numeric) -> BigDecimal
319
+ #
320
+ # Computes the arctangent of y and x to the specified number of digits of
321
+ # precision, +numeric+.
322
+ #
323
+ # BigMath.atan2(BigDecimal('-1'), BigDecimal('1'), 32).to_s
324
+ # #=> "-0.78539816339744830961566084581988e0"
325
+ #
326
+ def atan2(y, x, prec)
327
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan2)
328
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan2)
329
+ y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :atan2)
330
+ return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan?
331
+
332
+ if x.infinite? || y.infinite?
333
+ one = BigDecimal(1)
334
+ zero = BigDecimal(0)
335
+ x = x.infinite? ? (x > 0 ? one : -one) : zero
336
+ y = y.infinite? ? (y > 0 ? one : -one) : y.sign * zero
337
+ end
338
+
339
+ return x.sign >= 0 ? BigDecimal(0) : y.sign * PI(prec) if y.zero?
340
+
341
+ y = -y if neg = y < 0
342
+ xlarge = y.abs < x.abs
343
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
344
+ if x > 0
345
+ v = xlarge ? atan(y.div(x, prec2), prec) : PI(prec2) / 2 - atan(x.div(y, prec2), prec2)
346
+ else
347
+ v = xlarge ? PI(prec2) - atan(-y.div(x, prec2), prec2) : PI(prec2) / 2 + atan(x.div(-y, prec2), prec2)
348
+ end
349
+ v.mult(neg ? -1 : 1, prec)
350
+ end
351
+
352
+ # call-seq:
353
+ # sinh(decimal, numeric) -> BigDecimal
354
+ #
355
+ # Computes the hyperbolic sine of +decimal+ to the specified number of digits of
356
+ # precision, +numeric+.
357
+ #
358
+ # If +decimal+ is NaN, returns NaN.
359
+ #
360
+ # BigMath.sinh(BigDecimal('1'), 32).to_s
361
+ # #=> "0.11752011936438014568823818505956e1"
362
+ #
363
+ def sinh(x, prec)
364
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :sinh)
365
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sinh)
366
+ return BigDecimal::Internal.nan_computation_result if x.nan?
367
+ return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
368
+
369
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
370
+ prec2 -= x.exponent if x.exponent < 0
371
+ e = exp(x, prec2)
372
+ (e - BigDecimal(1).div(e, prec2)).div(2, prec)
373
+ end
374
+
375
+ # call-seq:
376
+ # cosh(decimal, numeric) -> BigDecimal
377
+ #
378
+ # Computes the hyperbolic cosine of +decimal+ to the specified number of digits of
379
+ # precision, +numeric+.
380
+ #
381
+ # If +decimal+ is NaN, returns NaN.
382
+ #
383
+ # BigMath.cosh(BigDecimal('1'), 32).to_s
384
+ # #=> "0.15430806348152437784779056207571e1"
385
+ #
386
+ def cosh(x, prec)
387
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :cosh)
388
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cosh)
389
+ return BigDecimal::Internal.nan_computation_result if x.nan?
390
+ return BigDecimal::Internal.infinity_computation_result if x.infinite?
391
+
392
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
393
+ e = exp(x, prec2)
394
+ (e + BigDecimal(1).div(e, prec2)).div(2, prec)
395
+ end
396
+
397
+ # call-seq:
398
+ # tanh(decimal, numeric) -> BigDecimal
399
+ #
400
+ # Computes the hyperbolic tangent of +decimal+ to the specified number of digits of
401
+ # precision, +numeric+.
402
+ #
403
+ # If +decimal+ is NaN, returns NaN.
404
+ #
405
+ # BigMath.tanh(BigDecimal('1'), 32).to_s
406
+ # #=> "0.76159415595576488811945828260479e0"
407
+ #
408
+ def tanh(x, prec)
409
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :tanh)
410
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :tanh)
411
+ return BigDecimal::Internal.nan_computation_result if x.nan?
412
+ return BigDecimal(x.infinite?) if x.infinite?
413
+
414
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC + [-x.exponent, 0].max
415
+ e = exp(x, prec2)
416
+ einv = BigDecimal(1).div(e, prec2)
417
+ (e - einv).div(e + einv, prec)
418
+ end
419
+
420
+ # call-seq:
421
+ # asinh(decimal, numeric) -> BigDecimal
422
+ #
423
+ # Computes the inverse hyperbolic sine of +decimal+ to the specified number of digits of
424
+ # precision, +numeric+.
425
+ #
426
+ # If +decimal+ is NaN, returns NaN.
427
+ #
428
+ # BigMath.asinh(BigDecimal('1'), 32).to_s
429
+ # #=> "0.88137358701954302523260932497979e0"
430
+ #
431
+ def asinh(x, prec)
432
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :asinh)
433
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asinh)
434
+ return BigDecimal::Internal.nan_computation_result if x.nan?
435
+ return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
436
+ return -asinh(-x, prec) if x < 0
437
+
438
+ sqrt_prec = prec + [-x.exponent, 0].max + BigDecimal::Internal::EXTRA_PREC
439
+ log(x + sqrt(x**2 + 1, sqrt_prec), prec)
440
+ end
441
+
442
+ # call-seq:
443
+ # acosh(decimal, numeric) -> BigDecimal
444
+ #
445
+ # Computes the inverse hyperbolic cosine of +decimal+ to the specified number of digits of
446
+ # precision, +numeric+.
447
+ #
448
+ # If +decimal+ is NaN, returns NaN.
449
+ #
450
+ # BigMath.acosh(BigDecimal('2'), 32).to_s
451
+ # #=> "0.1316957896924816708625046347308e1"
452
+ #
453
+ def acosh(x, prec)
454
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :acosh)
455
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acosh)
456
+ raise Math::DomainError, "Out of domain argument for acosh" if x < 1
457
+ return BigDecimal::Internal.infinity_computation_result if x.infinite?
458
+ return BigDecimal::Internal.nan_computation_result if x.nan?
459
+
460
+ log(x + sqrt(x**2 - 1, prec + BigDecimal::Internal::EXTRA_PREC), prec)
461
+ end
462
+
463
+ # call-seq:
464
+ # atanh(decimal, numeric) -> BigDecimal
465
+ #
466
+ # Computes the inverse hyperbolic tangent of +decimal+ to the specified number of digits of
467
+ # precision, +numeric+.
468
+ #
469
+ # If +decimal+ is NaN, returns NaN.
470
+ #
471
+ # BigMath.atanh(BigDecimal('0.5'), 32).to_s
472
+ # #=> "0.54930614433405484569762261846126e0"
473
+ #
474
+ def atanh(x, prec)
475
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :atanh)
476
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atanh)
477
+ raise Math::DomainError, "Out of domain argument for atanh" if x < -1 || x > 1
478
+ return BigDecimal::Internal.nan_computation_result if x.nan?
479
+ return BigDecimal::Internal.infinity_computation_result if x == 1
480
+ return -BigDecimal::Internal.infinity_computation_result if x == -1
481
+
482
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC
483
+ (log(x + 1, prec2) - log(1 - x, prec2)).div(2, prec)
484
+ end
485
+
486
+ # call-seq:
487
+ # BigMath.log2(decimal, numeric) -> BigDecimal
488
+ #
489
+ # Computes the base 2 logarithm of +decimal+ to the specified number of
490
+ # digits of precision, +numeric+.
491
+ #
492
+ # If +decimal+ is zero or negative, raises Math::DomainError.
493
+ #
494
+ # If +decimal+ is positive infinity, returns Infinity.
495
+ #
496
+ # If +decimal+ is NaN, returns NaN.
497
+ #
498
+ # BigMath.log2(BigDecimal('3'), 32).to_s
499
+ # #=> "0.15849625007211561814537389439478e1"
500
+ #
501
+ def log2(x, prec)
502
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :log2)
503
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log2)
504
+ return BigDecimal::Internal.nan_computation_result if x.nan?
505
+ return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
506
+
507
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC * 3 / 2
508
+ v = log(x, prec2).div(log(BigDecimal(2), prec2), prec2)
509
+ # Perform half-up rounding to calculate log2(2**n)==n correctly in every rounding mode
510
+ v = v.round(prec + BigDecimal::Internal::EXTRA_PREC - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
511
+ v.mult(1, prec)
512
+ end
513
+
514
+ # call-seq:
515
+ # BigMath.log10(decimal, numeric) -> BigDecimal
516
+ #
517
+ # Computes the base 10 logarithm of +decimal+ to the specified number of
518
+ # digits of precision, +numeric+.
519
+ #
520
+ # If +decimal+ is zero or negative, raises Math::DomainError.
521
+ #
522
+ # If +decimal+ is positive infinity, returns Infinity.
523
+ #
524
+ # If +decimal+ is NaN, returns NaN.
525
+ #
526
+ # BigMath.log10(BigDecimal('3'), 32).to_s
527
+ # #=> "0.47712125471966243729502790325512e0"
528
+ #
529
+ def log10(x, prec)
530
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :log10)
531
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log10)
532
+ return BigDecimal::Internal.nan_computation_result if x.nan?
533
+ return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
534
+
535
+ prec2 = prec + BigDecimal::Internal::EXTRA_PREC * 3 / 2
536
+ v = log(x, prec2).div(log(BigDecimal(10), prec2), prec2)
537
+ # Perform half-up rounding to calculate log10(10**n)==n correctly in every rounding mode
538
+ v = v.round(prec + BigDecimal::Internal::EXTRA_PREC - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
539
+ v.mult(1, prec)
540
+ end
541
+
542
+ # call-seq:
543
+ # BigMath.log1p(decimal, numeric) -> BigDecimal
544
+ #
545
+ # Computes log(1 + decimal) to the specified number of digits of precision, +numeric+.
546
+ #
547
+ # BigMath.log1p(BigDecimal('0.1'), 32).to_s
548
+ # #=> "0.95310179804324860043952123280765e-1"
549
+ #
550
+ def log1p(x, prec)
551
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :log1p)
552
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log1p)
553
+ raise Math::DomainError, 'Out of domain argument for log1p' if x < -1
554
+
555
+ return log(x + 1, prec)
556
+ end
557
+
558
+ # call-seq:
559
+ # BigMath.expm1(decimal, numeric) -> BigDecimal
560
+ #
561
+ # Computes exp(decimal) - 1 to the specified number of digits of precision, +numeric+.
562
+ #
563
+ # BigMath.expm1(BigDecimal('0.1'), 32).to_s
564
+ # #=> "0.10517091807564762481170782649025e0"
565
+ #
566
+ def expm1(x, prec)
567
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :expm1)
568
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :expm1)
569
+ return BigDecimal(-1) if x.infinite? == -1
570
+
571
+ exp_prec = prec
572
+ if x < -1
573
+ # log10(exp(x)) = x * log10(e)
574
+ lg_e = 0.4342944819032518
575
+ exp_prec = prec + (lg_e * x).ceil + BigDecimal::Internal::EXTRA_PREC
576
+ elsif x < 1
577
+ exp_prec = prec - x.exponent + BigDecimal::Internal::EXTRA_PREC
578
+ else
579
+ exp_prec = prec
580
+ end
581
+
582
+ return BigDecimal(-1) if exp_prec <= 0
583
+
584
+ exp(x, exp_prec).sub(1, prec)
585
+ end
586
+
587
+ # call-seq:
588
+ # erf(decimal, numeric) -> BigDecimal
589
+ #
590
+ # Computes the error function of +decimal+ to the specified number of digits of
591
+ # precision, +numeric+.
592
+ #
593
+ # If +decimal+ is NaN, returns NaN.
594
+ #
595
+ # BigMath.erf(BigDecimal('1'), 32).to_s
596
+ # #=> "0.84270079294971486934122063508261e0"
597
+ #
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 = x.to_f
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)
621
+ end
622
+
623
+ # call-seq:
624
+ # erfc(decimal, numeric) -> BigDecimal
625
+ #
626
+ # Computes the complementary error function of +decimal+ to the specified number of digits of
627
+ # precision, +numeric+.
628
+ #
629
+ # If +decimal+ is NaN, returns NaN.
630
+ #
631
+ # BigMath.erfc(BigDecimal('10'), 32).to_s
632
+ # #=> "0.20884875837625447570007862949578e-44"
633
+ #
634
+ 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::Internal.underflow_computation_result 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 = x.to_f
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 = x.to_f
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)
716
+ end
717
+
718
+ # call-seq:
719
+ # BigMath.gamma(decimal, numeric) -> BigDecimal
720
+ #
721
+ # Computes the gamma function of +decimal+ to the specified number of
722
+ # digits of precision, +numeric+.
723
+ #
724
+ # BigMath.gamma(BigDecimal('0.5'), 32).to_s
725
+ # #=> "0.17724538509055160272981674833411e1"
726
+ #
727
+ 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)
744
+ end
745
+
746
+ # call-seq:
747
+ # BigMath.lgamma(decimal, numeric) -> [BigDecimal, Integer]
748
+ #
749
+ # Computes the natural logarithm of the absolute value of the gamma function
750
+ # of +decimal+ to the specified number of digits of precision, +numeric+ and its sign.
751
+ #
752
+ # BigMath.lgamma(BigDecimal('0.5'), 32)
753
+ # #=> [0.57236494292470008707171367567653e0, 1]
754
+ #
755
+ 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)
856
+ end
857
+
858
+ # call-seq:
859
+ # frexp(x) -> [BigDecimal, Integer]
860
+ #
861
+ # Decomposes +x+ into a normalized fraction and an integral power of ten.
862
+ #
863
+ # BigMath.frexp(BigDecimal(123.456))
864
+ # #=> [0.123456e0, 3]
865
+ #
866
+ def frexp(x)
867
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :frexp)
868
+ return [x, 0] unless x.finite?
869
+
870
+ exponent = x.exponent
871
+ [x._decimal_shift(-exponent), exponent]
872
+ end
873
+
874
+ # call-seq:
875
+ # ldexp(fraction, exponent) -> BigDecimal
876
+ #
877
+ # Inverse of +frexp+.
878
+ # Returns the value of fraction * 10**exponent.
879
+ #
880
+ # BigMath.ldexp(BigDecimal("0.123456e0"), 3)
881
+ # #=> 0.123456e3
882
+ #
883
+ def ldexp(x, exponent)
884
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :ldexp)
885
+ x.finite? ? x._decimal_shift(exponent) : x
189
886
  end
190
887
 
191
888
  # call-seq:
@@ -198,39 +895,20 @@ module BigMath
198
895
  # #=> "0.31415926535897932384626433832795e1"
199
896
  #
200
897
  def PI(prec)
898
+ # Gauss–Legendre algorithm
201
899
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :PI)
202
- n = prec + BigDecimal.double_fig
203
- zero = BigDecimal("0")
204
- one = BigDecimal("1")
205
- two = BigDecimal("2")
206
-
207
- m25 = BigDecimal("-0.04")
208
- m57121 = BigDecimal("-57121")
209
-
210
- pi = zero
211
-
212
- d = one
213
- k = one
214
- t = BigDecimal("-80")
215
- while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
216
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
217
- t = t*m25
218
- d = t.div(k,m)
219
- k = k+two
220
- pi = pi + d
221
- end
222
-
223
- d = one
224
- k = one
225
- t = BigDecimal("956")
226
- while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
227
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
228
- t = t.div(m57121,n)
229
- d = t.div(k,m)
230
- pi = pi + d
231
- 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
232
910
  end
233
- pi.mult(1, prec)
911
+ (a * b).div(s, prec)
234
912
  end
235
913
 
236
914
  # call-seq:
@@ -244,6 +922,6 @@ module BigMath
244
922
  #
245
923
  def E(prec)
246
924
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :E)
247
- BigMath.exp(1, prec)
925
+ exp(1, prec)
248
926
  end
249
927
  end