bigdecimal 3.3.1 → 4.1.3

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,382 @@ 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
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
184
580
  end
185
- y *= 2 if dbl
186
- y = pi / 2 - y if inv
187
- y = -y if neg
188
- y.mult(1, prec)
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
+ require 'bigdecimal/math/erf'
600
+ Erf.erf(x, prec)
601
+ end
602
+
603
+ # call-seq:
604
+ # erfc(decimal, numeric) -> BigDecimal
605
+ #
606
+ # Computes the complementary error function of +decimal+ to the specified number of digits of
607
+ # precision, +numeric+.
608
+ #
609
+ # If +decimal+ is NaN, returns NaN.
610
+ #
611
+ # BigMath.erfc(BigDecimal('10'), 32).to_s
612
+ # #=> "0.20884875837625447570007862949578e-44"
613
+ #
614
+ def erfc(x, prec)
615
+ require 'bigdecimal/math/erf'
616
+ Erf.erfc(x, prec)
617
+ end
618
+
619
+ # call-seq:
620
+ # BigMath.gamma(decimal, numeric) -> BigDecimal
621
+ #
622
+ # Computes the gamma function of +decimal+ to the specified number of
623
+ # digits of precision, +numeric+.
624
+ #
625
+ # BigMath.gamma(BigDecimal('0.5'), 32).to_s
626
+ # #=> "0.17724538509055160272981674833411e1"
627
+ #
628
+ def gamma(x, prec)
629
+ require 'bigdecimal/math/gamma'
630
+ Gamma.gamma(x, prec)
631
+ end
632
+
633
+ # call-seq:
634
+ # BigMath.lgamma(decimal, numeric) -> [BigDecimal, Integer]
635
+ #
636
+ # Computes the natural logarithm of the absolute value of the gamma function
637
+ # of +decimal+ to the specified number of digits of precision, +numeric+ and its sign.
638
+ #
639
+ # BigMath.lgamma(BigDecimal('0.5'), 32)
640
+ # #=> [0.57236494292470008707171367567653e0, 1]
641
+ #
642
+ def lgamma(x, prec)
643
+ require 'bigdecimal/math/gamma'
644
+ Gamma.lgamma(x, prec)
645
+ end
646
+
647
+ # call-seq:
648
+ # frexp(x) -> [BigDecimal, Integer]
649
+ #
650
+ # Decomposes +x+ into a normalized fraction and an integral power of ten.
651
+ #
652
+ # BigMath.frexp(BigDecimal(123.456))
653
+ # #=> [0.123456e0, 3]
654
+ #
655
+ def frexp(x)
656
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :frexp)
657
+ return [x, 0] unless x.finite?
658
+
659
+ exponent = x.exponent
660
+ [x._decimal_shift(-exponent), exponent]
661
+ end
662
+
663
+ # call-seq:
664
+ # ldexp(fraction, exponent) -> BigDecimal
665
+ #
666
+ # Inverse of +frexp+.
667
+ # Returns the value of fraction * 10**exponent.
668
+ #
669
+ # BigMath.ldexp(BigDecimal("0.123456e0"), 3)
670
+ # #=> 0.123456e3
671
+ #
672
+ def ldexp(x, exponent)
673
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :ldexp)
674
+ x.finite? ? x._decimal_shift(exponent) : x
189
675
  end
190
676
 
191
677
  # call-seq:
@@ -198,39 +684,20 @@ module BigMath
198
684
  # #=> "0.31415926535897932384626433832795e1"
199
685
  #
200
686
  def PI(prec)
687
+ # Gauss–Legendre algorithm
201
688
  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
689
+ n = prec + BigDecimal::Internal::EXTRA_PREC
690
+ a = BigDecimal(1)
691
+ b = BigDecimal(0.5, 0).sqrt(n)
692
+ s = BigDecimal(0.25, 0)
693
+ t = 1
694
+ while a != b && (a - b).exponent > 1 - n
695
+ c = (a - b).div(2, n)
696
+ a, b = (a + b).div(2, n), (a * b).sqrt(n)
697
+ s = s.sub(c * c * t, n)
698
+ t *= 2
232
699
  end
233
- pi.mult(1, prec)
700
+ (a * b).div(s, prec)
234
701
  end
235
702
 
236
703
  # call-seq:
@@ -244,6 +711,6 @@ module BigMath
244
711
  #
245
712
  def E(prec)
246
713
  prec = BigDecimal::Internal.coerce_validate_prec(prec, :E)
247
- BigMath.exp(1, prec)
714
+ exp(1, prec)
248
715
  end
249
716
  end