bigdecimal 3.2.2 → 4.0.0

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.
@@ -5,14 +5,36 @@ require 'bigdecimal'
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)
12
+ # tan (x, prec)
13
+ # asin(x, prec)
14
+ # acos(x, prec)
10
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)
11
33
  # PI (prec)
12
34
  # E (prec) == exp(1.0,prec)
13
35
  #
14
36
  # where:
15
- # x ... BigDecimal number to be computed.
37
+ # x, y ... BigDecimal number to be computed.
16
38
  # prec ... Number of digits to be obtained.
17
39
  #++
18
40
  #
@@ -24,8 +46,8 @@ require 'bigdecimal'
24
46
  #
25
47
  # include BigMath
26
48
  #
27
- # a = BigDecimal((PI(100)/2).to_s)
28
- # puts sin(a,100) # => 0.99999999999999999999......e0
49
+ # a = BigDecimal((PI(49)/2).to_s)
50
+ # puts sin(a,100) # => 0.9999999999...9999999986e0
29
51
  #
30
52
  module BigMath
31
53
  module_function
@@ -36,13 +58,89 @@ module BigMath
36
58
  # Computes the square root of +decimal+ to the specified number of digits of
37
59
  # precision, +numeric+.
38
60
  #
39
- # BigMath.sqrt(BigDecimal('2'), 16).to_s
40
- # #=> "0.1414213562373095048801688724e1"
61
+ # BigMath.sqrt(BigDecimal('2'), 32).to_s
62
+ # #=> "0.14142135623730950488016887242097e1"
41
63
  #
42
64
  def sqrt(x, prec)
65
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :sqrt)
66
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sqrt)
43
67
  x.sqrt(prec)
44
68
  end
45
69
 
70
+
71
+ # Returns [sign, reduced_x] where reduced_x is in -pi/2..pi/2
72
+ # and satisfies sin(x) = sign * sin(reduced_x)
73
+ # If add_half_pi is true, adds pi/2 to x before reduction.
74
+ # Precision of pi is adjusted to ensure reduced_x has the required precision.
75
+ private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc:
76
+ return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi
77
+
78
+ mod_prec = prec + BigDecimal.double_fig
79
+ pi_extra_prec = [x.exponent, 0].max + BigDecimal.double_fig
80
+ while true
81
+ pi = PI(mod_prec + pi_extra_prec)
82
+ half_pi = pi / 2
83
+ div, mod = (add_half_pi ? x + pi : x + half_pi).divmod(pi)
84
+ mod -= half_pi
85
+ if mod.zero? || mod_prec + mod.exponent <= 0
86
+ # mod is too small to estimate required pi precision
87
+ mod_prec = mod_prec * 3 / 2 + BigDecimal.double_fig
88
+ elsif mod_prec + mod.exponent < prec
89
+ # Estimate required precision of pi
90
+ mod_prec = prec - mod.exponent + BigDecimal.double_fig
91
+ else
92
+ return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
93
+ end
94
+ end
95
+ end
96
+
97
+ # call-seq:
98
+ # cbrt(decimal, numeric) -> BigDecimal
99
+ #
100
+ # Computes the cube root of +decimal+ to the specified number of digits of
101
+ # precision, +numeric+.
102
+ #
103
+ # BigMath.cbrt(BigDecimal('2'), 32).to_s
104
+ # #=> "0.12599210498948731647672106072782e1"
105
+ #
106
+ def cbrt(x, prec)
107
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :cbrt)
108
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cbrt)
109
+ return BigDecimal::Internal.nan_computation_result if x.nan?
110
+ return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
111
+ return BigDecimal(0) if x.zero?
112
+
113
+ x = -x if neg = x < 0
114
+ ex = x.exponent / 3
115
+ x = x._decimal_shift(-3 * ex)
116
+ y = BigDecimal(Math.cbrt(x.to_f), 0)
117
+ precs = [prec + BigDecimal.double_fig]
118
+ precs << 2 + precs.last / 2 while precs.last > BigDecimal.double_fig
119
+ precs.reverse_each do |p|
120
+ y = (2 * y + x.div(y, p).div(y, p)).div(3, p)
121
+ end
122
+ y._decimal_shift(ex).mult(neg ? -1 : 1, prec)
123
+ end
124
+
125
+ # call-seq:
126
+ # hypot(x, y, numeric) -> BigDecimal
127
+ #
128
+ # Returns sqrt(x**2 + y**2) to the specified number of digits of
129
+ # precision, +numeric+.
130
+ #
131
+ # BigMath.hypot(BigDecimal('1'), BigDecimal('2'), 32).to_s
132
+ # #=> "0.22360679774997896964091736687313e1"
133
+ #
134
+ def hypot(x, y, prec)
135
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :hypot)
136
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :hypot)
137
+ y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :hypot)
138
+ return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan?
139
+ return BigDecimal::Internal.infinity_computation_result if x.infinite? || y.infinite?
140
+ prec2 = prec + BigDecimal.double_fig
141
+ sqrt(x.mult(x, prec2) + y.mult(y, prec2), prec)
142
+ end
143
+
46
144
  # call-seq:
47
145
  # sin(decimal, numeric) -> BigDecimal
48
146
  #
@@ -51,40 +149,33 @@ module BigMath
51
149
  #
52
150
  # If +decimal+ is Infinity or NaN, returns NaN.
53
151
  #
54
- # BigMath.sin(BigMath.PI(5)/4, 5).to_s
55
- # #=> "0.70710678118654752440082036563292800375e0"
152
+ # BigMath.sin(BigMath.PI(5)/4, 32).to_s
153
+ # #=> "0.70710807985947359435812921837984e0"
56
154
  #
57
155
  def sin(x, prec)
58
- raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
59
- return BigDecimal("NaN") if x.infinite? || x.nan?
156
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :sin)
157
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
158
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
60
159
  n = prec + BigDecimal.double_fig
61
160
  one = BigDecimal("1")
62
161
  two = BigDecimal("2")
63
- x = -x if neg = x < 0
64
- if x > (twopi = two * BigMath.PI(prec))
65
- if x > 30
66
- x %= twopi
67
- else
68
- x -= twopi while x > twopi
69
- end
70
- end
162
+ sign, x = _sin_periodic_reduction(x, n)
71
163
  x1 = x
72
164
  x2 = x.mult(x,n)
73
- sign = 1
74
165
  y = x
75
166
  d = y
76
167
  i = one
77
168
  z = one
78
169
  while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
79
170
  m = BigDecimal.double_fig if m < BigDecimal.double_fig
80
- sign = -sign
81
- x1 = x2.mult(x1,n)
171
+ x1 = -x2.mult(x1,n)
82
172
  i += two
83
173
  z *= (i-one) * i
84
- d = sign * x1.div(z,m)
174
+ d = x1.div(z,m)
85
175
  y += d
86
176
  end
87
- neg ? -y : y
177
+ y = BigDecimal("1") if y > 1
178
+ y.mult(sign, prec)
88
179
  end
89
180
 
90
181
  # call-seq:
@@ -95,40 +186,85 @@ module BigMath
95
186
  #
96
187
  # If +decimal+ is Infinity or NaN, returns NaN.
97
188
  #
98
- # BigMath.cos(BigMath.PI(4), 16).to_s
99
- # #=> "-0.999999999999999999999999999999856613163740061349e0"
189
+ # BigMath.cos(BigMath.PI(16), 32).to_s
190
+ # #=> "-0.99999999999999999999999999999997e0"
100
191
  #
101
192
  def cos(x, prec)
102
- raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
103
- return BigDecimal("NaN") if x.infinite? || x.nan?
104
- n = prec + BigDecimal.double_fig
105
- one = BigDecimal("1")
106
- two = BigDecimal("2")
107
- x = -x if x < 0
108
- if x > (twopi = two * BigMath.PI(prec))
109
- if x > 30
110
- x %= twopi
111
- else
112
- x -= twopi while x > twopi
113
- end
114
- end
115
- x1 = one
116
- x2 = x.mult(x,n)
117
- sign = 1
118
- y = one
119
- d = y
120
- i = BigDecimal("0")
121
- z = one
122
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
123
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
124
- sign = -sign
125
- x1 = x2.mult(x1,n)
126
- i += two
127
- z *= (i-one) * i
128
- d = sign * x1.div(z,m)
129
- y += d
193
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :cos)
194
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
195
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
196
+ sign, x = _sin_periodic_reduction(x, prec + BigDecimal.double_fig, add_half_pi: true)
197
+ sign * sin(x, prec)
198
+ end
199
+
200
+ # call-seq:
201
+ # tan(decimal, numeric) -> BigDecimal
202
+ #
203
+ # Computes the tangent of +decimal+ to the specified number of digits of
204
+ # precision, +numeric+.
205
+ #
206
+ # If +decimal+ is Infinity or NaN, returns NaN.
207
+ #
208
+ # BigMath.tan(BigDecimal("0.0"), 4).to_s
209
+ # #=> "0.0"
210
+ #
211
+ # BigMath.tan(BigMath.PI(24) / 4, 32).to_s
212
+ # #=> "0.99999999999999999999999830836025e0"
213
+ #
214
+ def tan(x, prec)
215
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :tan)
216
+ sin(x, prec + BigDecimal.double_fig).div(cos(x, prec + BigDecimal.double_fig), prec)
217
+ end
218
+
219
+ # call-seq:
220
+ # asin(decimal, numeric) -> BigDecimal
221
+ #
222
+ # Computes the arcsine of +decimal+ to the specified number of digits of
223
+ # precision, +numeric+.
224
+ #
225
+ # If +decimal+ is NaN, returns NaN.
226
+ #
227
+ # BigMath.asin(BigDecimal('0.5'), 32).to_s
228
+ # #=> "0.52359877559829887307710723054658e0"
229
+ #
230
+ def asin(x, prec)
231
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :asin)
232
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asin)
233
+ raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1
234
+ return BigDecimal::Internal.nan_computation_result if x.nan?
235
+
236
+ prec2 = prec + BigDecimal.double_fig
237
+ cos = (1 - x**2).sqrt(prec2)
238
+ if cos.zero?
239
+ PI(prec2).div(x > 0 ? 2 : -2, prec)
240
+ else
241
+ atan(x.div(cos, prec2), prec)
130
242
  end
131
- y
243
+ end
244
+
245
+ # call-seq:
246
+ # acos(decimal, numeric) -> BigDecimal
247
+ #
248
+ # Computes the arccosine of +decimal+ to the specified number of digits of
249
+ # precision, +numeric+.
250
+ #
251
+ # If +decimal+ is NaN, returns NaN.
252
+ #
253
+ # BigMath.acos(BigDecimal('0.5'), 32).to_s
254
+ # #=> "0.10471975511965977461542144610932e1"
255
+ #
256
+ def acos(x, prec)
257
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :acos)
258
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acos)
259
+ raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1
260
+ return BigDecimal::Internal.nan_computation_result if x.nan?
261
+
262
+ prec2 = prec + BigDecimal.double_fig
263
+ return (PI(prec2) / 2).sub(asin(x, prec2), prec) if x < 0
264
+ return PI(prec2).div(2, prec) if x.zero?
265
+
266
+ sin = (1 - x**2).sqrt(prec2)
267
+ atan(sin.div(x, prec2), prec)
132
268
  end
133
269
 
134
270
  # call-seq:
@@ -139,19 +275,20 @@ module BigMath
139
275
  #
140
276
  # If +decimal+ is NaN, returns NaN.
141
277
  #
142
- # BigMath.atan(BigDecimal('-1'), 16).to_s
143
- # #=> "-0.785398163397448309615660845819878471907514682065e0"
278
+ # BigMath.atan(BigDecimal('-1'), 32).to_s
279
+ # #=> "-0.78539816339744830961566084581988e0"
144
280
  #
145
281
  def atan(x, prec)
146
- raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
147
- return BigDecimal("NaN") if x.nan?
148
- pi = PI(prec)
282
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan)
283
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
284
+ return BigDecimal::Internal.nan_computation_result if x.nan?
285
+ n = prec + BigDecimal.double_fig
286
+ pi = PI(n)
149
287
  x = -x if neg = x < 0
150
288
  return pi.div(neg ? -2 : 2, prec) if x.infinite?
151
- return pi / (neg ? -4 : 4) if x.round(prec) == 1
152
- x = BigDecimal("1").div(x, prec) if inv = x > 1
153
- x = (-1 + sqrt(1 + x**2, prec))/x if dbl = x > 0.5
154
- n = prec + BigDecimal.double_fig
289
+ return pi.div(neg ? -4 : 4, prec) if x.round(n) == 1
290
+ x = BigDecimal("1").div(x, n) if inv = x > 1
291
+ x = (-1 + sqrt(1 + x.mult(x, n), n)).div(x, n) if dbl = x > 0.5
155
292
  y = x
156
293
  d = y
157
294
  t = x
@@ -167,7 +304,588 @@ module BigMath
167
304
  y *= 2 if dbl
168
305
  y = pi / 2 - y if inv
169
306
  y = -y if neg
170
- y
307
+ y.mult(1, prec)
308
+ end
309
+
310
+ # call-seq:
311
+ # atan2(decimal, decimal, numeric) -> BigDecimal
312
+ #
313
+ # Computes the arctangent of y and x to the specified number of digits of
314
+ # precision, +numeric+.
315
+ #
316
+ # BigMath.atan2(BigDecimal('-1'), BigDecimal('1'), 32).to_s
317
+ # #=> "-0.78539816339744830961566084581988e0"
318
+ #
319
+ def atan2(y, x, prec)
320
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan2)
321
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan2)
322
+ y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :atan2)
323
+ return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan?
324
+
325
+ if x.infinite? || y.infinite?
326
+ one = BigDecimal(1)
327
+ zero = BigDecimal(0)
328
+ x = x.infinite? ? (x > 0 ? one : -one) : zero
329
+ y = y.infinite? ? (y > 0 ? one : -one) : y.sign * zero
330
+ end
331
+
332
+ return x.sign >= 0 ? BigDecimal(0) : y.sign * PI(prec) if y.zero?
333
+
334
+ y = -y if neg = y < 0
335
+ xlarge = y.abs < x.abs
336
+ prec2 = prec + BigDecimal.double_fig
337
+ if x > 0
338
+ v = xlarge ? atan(y.div(x, prec2), prec) : PI(prec2) / 2 - atan(x.div(y, prec2), prec2)
339
+ else
340
+ v = xlarge ? PI(prec2) - atan(-y.div(x, prec2), prec2) : PI(prec2) / 2 + atan(x.div(-y, prec2), prec2)
341
+ end
342
+ v.mult(neg ? -1 : 1, prec)
343
+ end
344
+
345
+ # call-seq:
346
+ # sinh(decimal, numeric) -> BigDecimal
347
+ #
348
+ # Computes the hyperbolic sine of +decimal+ to the specified number of digits of
349
+ # precision, +numeric+.
350
+ #
351
+ # If +decimal+ is NaN, returns NaN.
352
+ #
353
+ # BigMath.sinh(BigDecimal('1'), 32).to_s
354
+ # #=> "0.11752011936438014568823818505956e1"
355
+ #
356
+ def sinh(x, prec)
357
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :sinh)
358
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sinh)
359
+ return BigDecimal::Internal.nan_computation_result if x.nan?
360
+ return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
361
+
362
+ prec2 = prec + BigDecimal.double_fig
363
+ prec2 -= x.exponent if x.exponent < 0
364
+ e = exp(x, prec2)
365
+ (e - BigDecimal(1).div(e, prec2)).div(2, prec)
366
+ end
367
+
368
+ # call-seq:
369
+ # cosh(decimal, numeric) -> BigDecimal
370
+ #
371
+ # Computes the hyperbolic cosine of +decimal+ to the specified number of digits of
372
+ # precision, +numeric+.
373
+ #
374
+ # If +decimal+ is NaN, returns NaN.
375
+ #
376
+ # BigMath.cosh(BigDecimal('1'), 32).to_s
377
+ # #=> "0.15430806348152437784779056207571e1"
378
+ #
379
+ def cosh(x, prec)
380
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :cosh)
381
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cosh)
382
+ return BigDecimal::Internal.nan_computation_result if x.nan?
383
+ return BigDecimal::Internal.infinity_computation_result if x.infinite?
384
+
385
+ prec2 = prec + BigDecimal.double_fig
386
+ e = exp(x, prec2)
387
+ (e + BigDecimal(1).div(e, prec2)).div(2, prec)
388
+ end
389
+
390
+ # call-seq:
391
+ # tanh(decimal, numeric) -> BigDecimal
392
+ #
393
+ # Computes the hyperbolic tangent of +decimal+ to the specified number of digits of
394
+ # precision, +numeric+.
395
+ #
396
+ # If +decimal+ is NaN, returns NaN.
397
+ #
398
+ # BigMath.tanh(BigDecimal('1'), 32).to_s
399
+ # #=> "0.76159415595576488811945828260479e0"
400
+ #
401
+ def tanh(x, prec)
402
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :tanh)
403
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :tanh)
404
+ return BigDecimal::Internal.nan_computation_result if x.nan?
405
+ return BigDecimal(x.infinite?) if x.infinite?
406
+
407
+ prec2 = prec + BigDecimal.double_fig + [-x.exponent, 0].max
408
+ e = exp(x, prec2)
409
+ einv = BigDecimal(1).div(e, prec2)
410
+ (e - einv).div(e + einv, prec)
411
+ end
412
+
413
+ # call-seq:
414
+ # asinh(decimal, numeric) -> BigDecimal
415
+ #
416
+ # Computes the inverse hyperbolic sine of +decimal+ to the specified number of digits of
417
+ # precision, +numeric+.
418
+ #
419
+ # If +decimal+ is NaN, returns NaN.
420
+ #
421
+ # BigMath.asinh(BigDecimal('1'), 32).to_s
422
+ # #=> "0.88137358701954302523260932497979e0"
423
+ #
424
+ def asinh(x, prec)
425
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :asinh)
426
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asinh)
427
+ return BigDecimal::Internal.nan_computation_result if x.nan?
428
+ return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
429
+ return -asinh(-x, prec) if x < 0
430
+
431
+ sqrt_prec = prec + [-x.exponent, 0].max + BigDecimal.double_fig
432
+ log(x + sqrt(x**2 + 1, sqrt_prec), prec)
433
+ end
434
+
435
+ # call-seq:
436
+ # acosh(decimal, numeric) -> BigDecimal
437
+ #
438
+ # Computes the inverse hyperbolic cosine of +decimal+ to the specified number of digits of
439
+ # precision, +numeric+.
440
+ #
441
+ # If +decimal+ is NaN, returns NaN.
442
+ #
443
+ # BigMath.acosh(BigDecimal('2'), 32).to_s
444
+ # #=> "0.1316957896924816708625046347308e1"
445
+ #
446
+ def acosh(x, prec)
447
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :acosh)
448
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acosh)
449
+ raise Math::DomainError, "Out of domain argument for acosh" if x < 1
450
+ return BigDecimal::Internal.infinity_computation_result if x.infinite?
451
+ return BigDecimal::Internal.nan_computation_result if x.nan?
452
+
453
+ log(x + sqrt(x**2 - 1, prec + BigDecimal.double_fig), prec)
454
+ end
455
+
456
+ # call-seq:
457
+ # atanh(decimal, numeric) -> BigDecimal
458
+ #
459
+ # Computes the inverse hyperbolic tangent of +decimal+ to the specified number of digits of
460
+ # precision, +numeric+.
461
+ #
462
+ # If +decimal+ is NaN, returns NaN.
463
+ #
464
+ # BigMath.atanh(BigDecimal('0.5'), 32).to_s
465
+ # #=> "0.54930614433405484569762261846126e0"
466
+ #
467
+ def atanh(x, prec)
468
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :atanh)
469
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atanh)
470
+ raise Math::DomainError, "Out of domain argument for atanh" if x < -1 || x > 1
471
+ return BigDecimal::Internal.nan_computation_result if x.nan?
472
+ return BigDecimal::Internal.infinity_computation_result if x == 1
473
+ return -BigDecimal::Internal.infinity_computation_result if x == -1
474
+
475
+ prec2 = prec + BigDecimal.double_fig
476
+ (log(x + 1, prec2) - log(1 - x, prec2)).div(2, prec)
477
+ end
478
+
479
+ # call-seq:
480
+ # BigMath.log2(decimal, numeric) -> BigDecimal
481
+ #
482
+ # Computes the base 2 logarithm of +decimal+ to the specified number of
483
+ # digits of precision, +numeric+.
484
+ #
485
+ # If +decimal+ is zero or negative, raises Math::DomainError.
486
+ #
487
+ # If +decimal+ is positive infinity, returns Infinity.
488
+ #
489
+ # If +decimal+ is NaN, returns NaN.
490
+ #
491
+ # BigMath.log2(BigDecimal('3'), 32).to_s
492
+ # #=> "0.15849625007211561814537389439478e1"
493
+ #
494
+ def log2(x, prec)
495
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :log2)
496
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log2)
497
+ return BigDecimal::Internal.nan_computation_result if x.nan?
498
+ return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
499
+
500
+ prec2 = prec + BigDecimal.double_fig * 3 / 2
501
+ v = log(x, prec2).div(log(BigDecimal(2), prec2), prec2)
502
+ # Perform half-up rounding to calculate log2(2**n)==n correctly in every rounding mode
503
+ v = v.round(prec + BigDecimal.double_fig - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
504
+ v.mult(1, prec)
505
+ end
506
+
507
+ # call-seq:
508
+ # BigMath.log10(decimal, numeric) -> BigDecimal
509
+ #
510
+ # Computes the base 10 logarithm of +decimal+ to the specified number of
511
+ # digits of precision, +numeric+.
512
+ #
513
+ # If +decimal+ is zero or negative, raises Math::DomainError.
514
+ #
515
+ # If +decimal+ is positive infinity, returns Infinity.
516
+ #
517
+ # If +decimal+ is NaN, returns NaN.
518
+ #
519
+ # BigMath.log10(BigDecimal('3'), 32).to_s
520
+ # #=> "0.47712125471966243729502790325512e0"
521
+ #
522
+ def log10(x, prec)
523
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :log10)
524
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log10)
525
+ return BigDecimal::Internal.nan_computation_result if x.nan?
526
+ return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
527
+
528
+ prec2 = prec + BigDecimal.double_fig * 3 / 2
529
+ v = log(x, prec2).div(log(BigDecimal(10), prec2), prec2)
530
+ # Perform half-up rounding to calculate log10(10**n)==n correctly in every rounding mode
531
+ v = v.round(prec + BigDecimal.double_fig - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
532
+ v.mult(1, prec)
533
+ end
534
+
535
+ # call-seq:
536
+ # BigMath.log1p(decimal, numeric) -> BigDecimal
537
+ #
538
+ # Computes log(1 + decimal) to the specified number of digits of precision, +numeric+.
539
+ #
540
+ # BigMath.log1p(BigDecimal('0.1'), 32).to_s
541
+ # #=> "0.95310179804324860043952123280765e-1"
542
+ #
543
+ def log1p(x, prec)
544
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :log1p)
545
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log1p)
546
+ raise Math::DomainError, 'Out of domain argument for log1p' if x < -1
547
+
548
+ return log(x + 1, prec)
549
+ end
550
+
551
+ # call-seq:
552
+ # BigMath.expm1(decimal, numeric) -> BigDecimal
553
+ #
554
+ # Computes exp(decimal) - 1 to the specified number of digits of precision, +numeric+.
555
+ #
556
+ # BigMath.expm1(BigDecimal('0.1'), 32).to_s
557
+ # #=> "0.10517091807564762481170782649025e0"
558
+ #
559
+ def expm1(x, prec)
560
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :expm1)
561
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :expm1)
562
+ return BigDecimal(-1) if x.infinite? == -1
563
+
564
+ exp_prec = prec
565
+ if x < -1
566
+ # log10(exp(x)) = x * log10(e)
567
+ lg_e = 0.4342944819032518
568
+ exp_prec = prec + (lg_e * x).ceil + BigDecimal.double_fig
569
+ elsif x < 1
570
+ exp_prec = prec - x.exponent + BigDecimal.double_fig
571
+ else
572
+ exp_prec = prec
573
+ end
574
+
575
+ return BigDecimal(-1) if exp_prec <= 0
576
+
577
+ exp = exp(x, exp_prec)
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
585
+ end
586
+
587
+ # erf(decimal, numeric) -> BigDecimal
588
+ #
589
+ # Computes the error function of +decimal+ to the specified number of digits of
590
+ # precision, +numeric+.
591
+ #
592
+ # If +decimal+ is NaN, returns NaN.
593
+ #
594
+ # BigMath.erf(BigDecimal('1'), 32).to_s
595
+ # #=> "0.84270079294971486934122063508261e0"
596
+ #
597
+ def erf(x, prec)
598
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :erf)
599
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erf)
600
+ return BigDecimal::Internal.nan_computation_result if x.nan?
601
+ return BigDecimal(x.infinite?) if x.infinite?
602
+ return BigDecimal(0) if x == 0
603
+ return -erf(-x, prec) if x < 0
604
+ return BigDecimal(1) if x > 5000000000 # erf(5000000000) > 1 - 1e-10000000000000000000
605
+
606
+ if x > 8
607
+ xf = x.to_f
608
+ log10_erfc = -xf ** 2 / Math.log(10) - Math.log10(xf * Math::PI ** 0.5)
609
+ erfc_prec = [prec + log10_erfc.ceil, 1].max
610
+ 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
617
+ end
618
+
619
+ prec2 = prec + BigDecimal.double_fig
620
+ x_smallprec = x.mult(1, Integer.sqrt(prec2) / 2)
621
+ # Taylor series of x with small precision is fast
622
+ erf1 = _erf_taylor(x_smallprec, BigDecimal(0), BigDecimal(0), prec2)
623
+ # Taylor series converges quickly for small x
624
+ _erf_taylor(x - x_smallprec, x_smallprec, erf1, prec2).mult(1, prec)
625
+ end
626
+
627
+ # call-seq:
628
+ # erfc(decimal, numeric) -> BigDecimal
629
+ #
630
+ # Computes the complementary error function of +decimal+ to the specified number of digits of
631
+ # precision, +numeric+.
632
+ #
633
+ # If +decimal+ is NaN, returns NaN.
634
+ #
635
+ # BigMath.erfc(BigDecimal('10'), 32).to_s
636
+ # #=> "0.20884875837625447570007862949578e-44"
637
+ #
638
+ def erfc(x, prec)
639
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :erfc)
640
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :erfc)
641
+ return BigDecimal::Internal.nan_computation_result if x.nan?
642
+ return BigDecimal(1 - x.infinite?) if x.infinite?
643
+ return BigDecimal(1).sub(erf(x, prec + BigDecimal.double_fig), prec) if x < 0
644
+ return BigDecimal(0) if x > 5000000000 # erfc(5000000000) < 1e-10000000000000000000 (underflow)
645
+
646
+ if x >= 8
647
+ y = _erfc_asymptotic(x, prec)
648
+ return y.mult(1, prec) if y
649
+ end
650
+
651
+ # erfc(x) = 1 - erf(x) < exp(-x**2)/x/sqrt(pi)
652
+ # Precision of erf(x) needs about log10(exp(-x**2)) extra digits
653
+ log10 = 2.302585092994046
654
+ high_prec = prec + BigDecimal.double_fig + (x.ceil**2 / log10).ceil
655
+ BigDecimal(1).sub(erf(x, high_prec), prec)
656
+ end
657
+
658
+ # Calculates erf(x + a)
659
+ private_class_method def _erf_taylor(x, a, erf_a, prec) # :nodoc:
660
+ return erf_a if x.zero?
661
+ # Let f(x+a) = erf(x+a)*exp((x+a)**2)*sqrt(pi)/2
662
+ # = c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...
663
+ # f'(x+a) = 1+2*(x+a)*f(x+a)
664
+ # f'(x+a) = c1 + 2*c2*x + 3*c3*x**2 + 4*c4*x**3 + 5*c5*x**4 + ...
665
+ # = 1+2*(x+a)*(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + ...)
666
+ # therefore,
667
+ # c0 = f(a)
668
+ # c1 = 2 * a * c0 + 1
669
+ # c2 = (2 * c0 + 2 * a * c1) / 2
670
+ # c3 = (2 * c1 + 2 * a * c2) / 3
671
+ # c4 = (2 * c2 + 2 * a * c3) / 4
672
+ #
673
+ # All coefficients are positive when a >= 0
674
+
675
+ scale = BigDecimal(2).div(sqrt(PI(prec), prec), prec)
676
+ c_prev = erf_a.div(scale.mult(exp(-a*a, prec), prec), prec)
677
+ c_next = (2 * a * c_prev).add(1, prec).mult(x, prec)
678
+ sum = c_prev.add(c_next, prec)
679
+
680
+ 2.step do |k|
681
+ c = (c_prev.mult(x, prec) + a * c_next).mult(2, prec).mult(x, prec).div(k, prec)
682
+ sum = sum.add(c, prec)
683
+ c_prev, c_next = c_next, c
684
+ break if [c_prev, c_next].all? { |c| c.zero? || (c.exponent < sum.exponent - prec) }
685
+ end
686
+ value = sum.mult(scale.mult(exp(-(x + a).mult(x + a, prec), prec), prec), prec)
687
+ value > 1 ? BigDecimal(1) : value
688
+ end
689
+
690
+ private_class_method def _erfc_asymptotic(x, prec) # :nodoc:
691
+ # Let f(x) = erfc(x)*sqrt(pi)*exp(x**2)/2
692
+ # f(x) satisfies the following differential equation:
693
+ # 2*x*f(x) = f'(x) + 1
694
+ # From the above equation, we can derive the following asymptotic expansion:
695
+ # f(x) = (0..kmax).sum { (-1)**k * (2*k)! / 4**k / k! / x**(2*k)) } / x
696
+
697
+ # This asymptotic expansion does not converge.
698
+ # But if there is a k that satisfies (2*k)! / 4**k / k! / x**(2*k) < 10**(-prec),
699
+ # It is enough to calculate erfc within the given precision.
700
+ # Using Stirling's approximation, we can simplify this condition to:
701
+ # sqrt(2)/2 + k*log(k) - k - 2*k*log(x) < -prec*log(10)
702
+ # and the left side is minimized when k = x**2.
703
+ prec += BigDecimal.double_fig
704
+ xf = x.to_f
705
+ kmax = (1..(xf ** 2).floor).bsearch do |k|
706
+ Math.log(2) / 2 + k * Math.log(k) - k - 2 * k * Math.log(xf) < -prec * Math.log(10)
707
+ end
708
+ return unless kmax
709
+
710
+ sum = BigDecimal(1)
711
+ x2 = x.mult(x, prec)
712
+ d = BigDecimal(1)
713
+ (1..kmax).each do |k|
714
+ d = d.div(x2, prec).mult(1 - 2 * k, prec).div(2, prec)
715
+ sum = sum.add(d, prec)
716
+ end
717
+ sum.div(exp(x2, prec).mult(PI(prec).sqrt(prec), prec), prec).div(x, prec)
718
+ end
719
+
720
+ # call-seq:
721
+ # BigMath.gamma(decimal, numeric) -> BigDecimal
722
+ #
723
+ # Computes the gamma function of +decimal+ to the specified number of
724
+ # digits of precision, +numeric+.
725
+ #
726
+ # BigMath.gamma(BigDecimal('0.5'), 32).to_s
727
+ # #=> "0.17724538509055160272981674833411e1"
728
+ #
729
+ def gamma(x, prec)
730
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :gamma)
731
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :gamma)
732
+ prec2 = prec + BigDecimal.double_fig
733
+ if x < 0.5
734
+ raise Math::DomainError, 'Numerical argument is out of domain - gamma' if x.frac.zero?
735
+
736
+ # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
737
+ pi = PI(prec2)
738
+ sin = _sinpix(x, pi, prec2)
739
+ return pi.div(gamma(1 - x, prec2).mult(sin, prec2), prec)
740
+ elsif x.frac.zero? && x < 1000 * prec
741
+ return _gamma_positive_integer(x, prec2).mult(1, prec)
742
+ end
743
+
744
+ a, sum = _gamma_spouge_sum_part(x, prec2)
745
+ (x + (a - 1)).power(x - 0.5, prec2).mult(BigMath.exp(1 - x, prec2), prec2).mult(sum, prec)
746
+ end
747
+
748
+ # call-seq:
749
+ # BigMath.lgamma(decimal, numeric) -> [BigDecimal, Integer]
750
+ #
751
+ # Computes the natural logarithm of the absolute value of the gamma function
752
+ # of +decimal+ to the specified number of digits of precision, +numeric+ and its sign.
753
+ #
754
+ # BigMath.lgamma(BigDecimal('0.5'), 32)
755
+ # #=> [0.57236494292470008707171367567653e0, 1]
756
+ #
757
+ def lgamma(x, prec)
758
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :lgamma)
759
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :lgamma)
760
+ prec2 = prec + BigDecimal.double_fig
761
+ if x < 0.5
762
+ return [BigDecimal::INFINITY, 1] if x.frac.zero?
763
+
764
+ loop do
765
+ # Euler's reflection formula: gamma(z) * gamma(1-z) = pi/sin(pi*z)
766
+ pi = PI(prec2)
767
+ sin = _sinpix(x, pi, prec2)
768
+ 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.double_fig
770
+
771
+ # Retry with higher precision if loss of significance is too large
772
+ prec2 = prec2 * 3 / 2
773
+ end
774
+ elsif x.frac.zero? && x < 1000 * prec
775
+ log_gamma = BigMath.log(_gamma_positive_integer(x, prec2), prec)
776
+ [log_gamma, 1]
777
+ else
778
+ # if x is close to 1 or 2, increase precision to reduce loss of significance
779
+ diff1_exponent = (x - 1).exponent
780
+ diff2_exponent = (x - 2).exponent
781
+ extra_prec = [-diff1_exponent, -diff2_exponent, 0].max
782
+ extremely_near_one = diff1_exponent < -prec2
783
+ extremely_near_two = diff2_exponent < -prec2
784
+
785
+ if extremely_near_one || extremely_near_two
786
+ # If x is extreamely close to base = 1 or 2, linear interpolation is accurate enough.
787
+ # Taylor expansion at x = base is: (x - base) * digamma(base) + (x - base) ** 2 * trigamma(base) / 2 + ...
788
+ # And we can ignore (x - base) ** 2 and higher order terms.
789
+ base = extremely_near_one ? 1 : 2
790
+ d = BigDecimal(1)._decimal_shift(1 - prec2)
791
+ log_gamma_d, sign = lgamma(base + d, prec2)
792
+ return [log_gamma_d.mult(x - base, prec2).div(d, prec), sign]
793
+ end
794
+
795
+ prec2 += [-diff1_exponent, -diff2_exponent, 0].max
796
+ a, sum = _gamma_spouge_sum_part(x, prec2)
797
+ log_gamma = BigMath.log(sum, prec2).add((x - 0.5).mult(BigMath.log(x.add(a - 1, prec2), prec2), prec2) + 1 - x, prec)
798
+ [log_gamma, 1]
799
+ end
800
+ end
801
+
802
+ # Returns sum part: sqrt(2*pi) and c[k]/(x+k) terms of Spouge's approximation
803
+ private_class_method def _gamma_spouge_sum_part(x, prec) # :nodoc:
804
+ x -= 1
805
+ # Spouge's approximation
806
+ # x! = (x + a)**(x + 0.5) * exp(-x - a) * (sqrt(2 * pi) + (1..a - 1).sum{|k| c[k] / (x + k) } + epsilon)
807
+ # where c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(a - k) / (k - 1)!
808
+ # and epsilon is bounded by a**(-0.5) * (2 * pi) ** (-a - 0.5)
809
+
810
+ # Estimate required a for given precision
811
+ a = (prec / Math.log10(2 * Math::PI)).ceil
812
+
813
+ # Calculate exponent of c[k] in low precision to estimate required precision
814
+ low_prec = 16
815
+ log10f = Math.log(10)
816
+ x_low_prec = x.mult(1, low_prec)
817
+ loggamma_k = 0
818
+ ck_exponents = (1..a-1).map do |k|
819
+ loggamma_k += Math.log10(k - 1) if k > 1
820
+ -loggamma_k - k / log10f + (k - 0.5) * Math.log10(a - k) - BigMath.log10(x_low_prec.add(k, low_prec), low_prec)
821
+ end
822
+
823
+ # Estimate exponent of sum by Stirling's approximation
824
+ 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))
825
+
826
+ # Determine required precision of c[k]
827
+ prec2 = [ck_exponents.max.ceil - approx_sum_exponent.floor, 0].max + prec
828
+
829
+ einv = BigMath.exp(-1, prec2)
830
+ sum = (PI(prec) * 2).sqrt(prec).mult(BigMath.exp(-a, prec), prec)
831
+ y = BigDecimal(1)
832
+ (1..a - 1).each do |k|
833
+ # c[k] = (-1)**k * (a - k)**(k - 0.5) * exp(-k) / (k-1)! / (x + k)
834
+ y = y.div(1 - k, prec2) if k > 1
835
+ y = y.mult(einv, prec2)
836
+ z = y.mult(BigDecimal((a - k) ** k), prec2).div(BigDecimal(a - k).sqrt(prec2).mult(x.add(k, prec2), prec2), prec2)
837
+ # sum += c[k] / (x + k)
838
+ sum = sum.add(z, prec2)
839
+ end
840
+ [a, sum]
841
+ end
842
+
843
+ private_class_method def _gamma_positive_integer(x, prec) # :nodoc:
844
+ return x if x == 1
845
+ numbers = (1..x - 1).map {|i| BigDecimal(i) }
846
+ while numbers.size > 1
847
+ numbers = numbers.each_slice(2).map {|a, b| b ? a.mult(b, prec) : a }
848
+ end
849
+ numbers.first
850
+ end
851
+
852
+ # Returns sin(pi * x), for gamma reflection formula calculation
853
+ private_class_method def _sinpix(x, pi, prec) # :nodoc:
854
+ x = x % 2
855
+ sign = x > 1 ? -1 : 1
856
+ x %= 1
857
+ x = 1 - x if x > 0.5 # to avoid sin(pi*x) loss of precision for x close to 1
858
+ sign * sin(x.mult(pi, prec), prec)
859
+ end
860
+
861
+ # call-seq:
862
+ # frexp(x) -> [BigDecimal, Integer]
863
+ #
864
+ # Decomposes +x+ into a normalized fraction and an integral power of ten.
865
+ #
866
+ # BigMath.frexp(BigDecimal(123.456))
867
+ # #=> [0.123456e0, 3]
868
+ #
869
+ def frexp(x)
870
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :frexp)
871
+ return [x, 0] unless x.finite?
872
+
873
+ exponent = x.exponent
874
+ [x._decimal_shift(-exponent), exponent]
875
+ end
876
+
877
+ # call-seq:
878
+ # ldexp(fraction, exponent) -> BigDecimal
879
+ #
880
+ # Inverse of +frexp+.
881
+ # Returns the value of fraction * 10**exponent.
882
+ #
883
+ # BigMath.ldexp(BigDecimal("0.123456e0"), 3)
884
+ # #=> 0.123456e3
885
+ #
886
+ def ldexp(x, exponent)
887
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :ldexp)
888
+ x.finite? ? x._decimal_shift(exponent) : x
171
889
  end
172
890
 
173
891
  # call-seq:
@@ -176,11 +894,11 @@ module BigMath
176
894
  # Computes the value of pi to the specified number of digits of precision,
177
895
  # +numeric+.
178
896
  #
179
- # BigMath.PI(10).to_s
180
- # #=> "0.3141592653589793238462643388813853786957412e1"
897
+ # BigMath.PI(32).to_s
898
+ # #=> "0.31415926535897932384626433832795e1"
181
899
  #
182
900
  def PI(prec)
183
- raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
901
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :PI)
184
902
  n = prec + BigDecimal.double_fig
185
903
  zero = BigDecimal("0")
186
904
  one = BigDecimal("1")
@@ -212,7 +930,7 @@ module BigMath
212
930
  pi = pi + d
213
931
  k = k+two
214
932
  end
215
- pi
933
+ pi.mult(1, prec)
216
934
  end
217
935
 
218
936
  # call-seq:
@@ -221,11 +939,11 @@ module BigMath
221
939
  # Computes e (the base of natural logarithms) to the specified number of
222
940
  # digits of precision, +numeric+.
223
941
  #
224
- # BigMath.E(10).to_s
225
- # #=> "0.271828182845904523536028752390026306410273e1"
942
+ # BigMath.E(32).to_s
943
+ # #=> "0.27182818284590452353602874713527e1"
226
944
  #
227
945
  def E(prec)
228
- raise ArgumentError, "Zero or negative precision for E" if prec <= 0
229
- BigMath.exp(1, prec)
946
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :E)
947
+ exp(1, prec)
230
948
  end
231
949
  end