bigdecimal 3.2.2 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +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)
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,118 @@ 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::Internal::EXTRA_PREC
79
+ pi_extra_prec = [x.exponent, 0].max + BigDecimal::Internal::EXTRA_PREC
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::Internal::EXTRA_PREC
88
+ elsif mod_prec + mod.exponent < prec
89
+ # Estimate required precision of pi
90
+ mod_prec = prec - mod.exponent + BigDecimal::Internal::EXTRA_PREC
91
+ else
92
+ return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
93
+ end
94
+ end
95
+ end
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(BigDecimal::Internal.fast_to_f(x)), 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
+
46
173
  # call-seq:
47
174
  # sin(decimal, numeric) -> BigDecimal
48
175
  #
@@ -51,40 +178,16 @@ module BigMath
51
178
  #
52
179
  # If +decimal+ is Infinity or NaN, returns NaN.
53
180
  #
54
- # BigMath.sin(BigMath.PI(5)/4, 5).to_s
55
- # #=> "0.70710678118654752440082036563292800375e0"
181
+ # BigMath.sin(BigMath.PI(5)/4, 32).to_s
182
+ # #=> "0.70710807985947359435812921837984e0"
56
183
  #
57
184
  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?
60
- n = prec + BigDecimal.double_fig
61
- one = BigDecimal("1")
62
- 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
71
- x1 = x
72
- x2 = x.mult(x,n)
73
- sign = 1
74
- y = x
75
- d = y
76
- i = one
77
- z = one
78
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
79
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
80
- sign = -sign
81
- x1 = x2.mult(x1,n)
82
- i += two
83
- z *= (i-one) * i
84
- d = sign * x1.div(z,m)
85
- y += d
86
- end
87
- neg ? -y : y
185
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :sin)
186
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
187
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
188
+ n = prec + BigDecimal::Internal::EXTRA_PREC
189
+ sign, x = _sin_periodic_reduction(x, n)
190
+ _sin_around_zero(x, n).mult(sign, prec)
88
191
  end
89
192
 
90
193
  # call-seq:
@@ -95,40 +198,87 @@ module BigMath
95
198
  #
96
199
  # If +decimal+ is Infinity or NaN, returns NaN.
97
200
  #
98
- # BigMath.cos(BigMath.PI(4), 16).to_s
99
- # #=> "-0.999999999999999999999999999999856613163740061349e0"
201
+ # BigMath.cos(BigMath.PI(16), 32).to_s
202
+ # #=> "-0.99999999999999999999999999999997e0"
100
203
  #
101
204
  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
205
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :cos)
206
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
207
+ return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
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)
211
+ end
212
+
213
+ # call-seq:
214
+ # tan(decimal, numeric) -> BigDecimal
215
+ #
216
+ # Computes the tangent of +decimal+ to the specified number of digits of
217
+ # precision, +numeric+.
218
+ #
219
+ # If +decimal+ is Infinity or NaN, returns NaN.
220
+ #
221
+ # BigMath.tan(BigDecimal("0.0"), 4).to_s
222
+ # #=> "0.0"
223
+ #
224
+ # BigMath.tan(BigMath.PI(24) / 4, 32).to_s
225
+ # #=> "0.99999999999999999999999830836025e0"
226
+ #
227
+ def tan(x, prec)
228
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :tan)
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)
130
256
  end
131
- y
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)
132
282
  end
133
283
 
134
284
  # call-seq:
@@ -139,80 +289,626 @@ module BigMath
139
289
  #
140
290
  # If +decimal+ is NaN, returns NaN.
141
291
  #
142
- # BigMath.atan(BigDecimal('-1'), 16).to_s
143
- # #=> "-0.785398163397448309615660845819878471907514682065e0"
292
+ # BigMath.atan(BigDecimal('-1'), 32).to_s
293
+ # #=> "-0.78539816339744830961566084581988e0"
144
294
  #
145
295
  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)
296
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan)
297
+ x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
298
+ return BigDecimal::Internal.nan_computation_result if x.nan?
299
+ n = prec + BigDecimal::Internal::EXTRA_PREC
300
+ return PI(n).div(2 * x.infinite?, prec) if x.infinite?
301
+
149
302
  x = -x if neg = x < 0
150
- 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
155
- y = x
156
- d = y
157
- t = x
158
- r = BigDecimal("3")
159
- x2 = x.mult(x,n)
160
- while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
161
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
162
- t = -t.mult(x2,n)
163
- d = t.div(r,m)
164
- y += d
165
- r += 2
166
- end
167
- y *= 2 if dbl
168
- y = pi / 2 - y if inv
169
- y = -y if neg
170
- y
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(BigDecimal::Internal.fast_to_f(x)), 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)
171
315
  end
172
316
 
173
317
  # call-seq:
174
- # PI(numeric) -> BigDecimal
318
+ # atan2(decimal, decimal, numeric) -> BigDecimal
175
319
  #
176
- # Computes the value of pi to the specified number of digits of precision,
177
- # +numeric+.
320
+ # Computes the arctangent of y and x to the specified number of digits of
321
+ # precision, +numeric+.
178
322
  #
179
- # BigMath.PI(10).to_s
180
- # #=> "0.3141592653589793238462643388813853786957412e1"
323
+ # BigMath.atan2(BigDecimal('-1'), BigDecimal('1'), 32).to_s
324
+ # #=> "-0.78539816339744830961566084581988e0"
181
325
  #
182
- def PI(prec)
183
- raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
184
- n = prec + BigDecimal.double_fig
185
- zero = BigDecimal("0")
186
- one = BigDecimal("1")
187
- two = BigDecimal("2")
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 = BigDecimal::Internal.fast_to_f(x)
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(0) 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 = BigDecimal::Internal.fast_to_f(x)
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 = BigDecimal::Internal.fast_to_f(x)
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
188
791
 
189
- m25 = BigDecimal("-0.04")
190
- m57121 = BigDecimal("-57121")
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)
191
806
 
192
- pi = zero
807
+ # Estimate required a for given precision
808
+ a = (prec / Math.log10(2 * Math::PI)).ceil
193
809
 
194
- d = one
195
- k = one
196
- t = BigDecimal("-80")
197
- while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
198
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
199
- t = t*m25
200
- d = t.div(k,m)
201
- k = k+two
202
- pi = pi + d
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
203
818
  end
204
819
 
205
- d = one
206
- k = one
207
- t = BigDecimal("956")
208
- while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
209
- m = BigDecimal.double_fig if m < BigDecimal.double_fig
210
- t = t.div(m57121,n)
211
- d = t.div(k,m)
212
- pi = pi + d
213
- k = k+two
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
886
+ end
887
+
888
+ # call-seq:
889
+ # PI(numeric) -> BigDecimal
890
+ #
891
+ # Computes the value of pi to the specified number of digits of precision,
892
+ # +numeric+.
893
+ #
894
+ # BigMath.PI(32).to_s
895
+ # #=> "0.31415926535897932384626433832795e1"
896
+ #
897
+ def PI(prec)
898
+ # Gauss–Legendre algorithm
899
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :PI)
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
214
910
  end
215
- pi
911
+ (a * b).div(s, prec)
216
912
  end
217
913
 
218
914
  # call-seq:
@@ -221,11 +917,11 @@ module BigMath
221
917
  # Computes e (the base of natural logarithms) to the specified number of
222
918
  # digits of precision, +numeric+.
223
919
  #
224
- # BigMath.E(10).to_s
225
- # #=> "0.271828182845904523536028752390026306410273e1"
920
+ # BigMath.E(32).to_s
921
+ # #=> "0.27182818284590452353602874713527e1"
226
922
  #
227
923
  def E(prec)
228
- raise ArgumentError, "Zero or negative precision for E" if prec <= 0
229
- BigMath.exp(1, prec)
924
+ prec = BigDecimal::Internal.coerce_validate_prec(prec, :E)
925
+ exp(1, prec)
230
926
  end
231
927
  end