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.
- checksums.yaml +4 -4
- data/ext/bigdecimal/bigdecimal.c +734 -2289
- data/ext/bigdecimal/bigdecimal.h +4 -25
- data/ext/bigdecimal/bits.h +3 -0
- data/ext/bigdecimal/extconf.rb +3 -7
- data/ext/bigdecimal/missing.h +1 -93
- data/lib/bigdecimal/jacobian.rb +2 -0
- data/lib/bigdecimal/ludcmp.rb +2 -0
- data/lib/bigdecimal/math.rb +789 -71
- data/lib/bigdecimal/newton.rb +2 -0
- data/lib/bigdecimal/util.rb +15 -14
- data/lib/bigdecimal.rb +355 -0
- metadata +2 -2
data/lib/bigdecimal/math.rb
CHANGED
|
@@ -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
|
|
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(
|
|
28
|
-
# puts sin(a,100) # => 0.
|
|
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'),
|
|
40
|
-
# #=> "0.
|
|
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,
|
|
55
|
-
# #=> "0.
|
|
152
|
+
# BigMath.sin(BigMath.PI(5)/4, 32).to_s
|
|
153
|
+
# #=> "0.70710807985947359435812921837984e0"
|
|
56
154
|
#
|
|
57
155
|
def sin(x, prec)
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
-
x1 = x2.mult(x1,n)
|
|
171
|
+
x1 = -x2.mult(x1,n)
|
|
82
172
|
i += two
|
|
83
173
|
z *= (i-one) * i
|
|
84
|
-
d =
|
|
174
|
+
d = x1.div(z,m)
|
|
85
175
|
y += d
|
|
86
176
|
end
|
|
87
|
-
|
|
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(
|
|
99
|
-
# #=> "-0.
|
|
189
|
+
# BigMath.cos(BigMath.PI(16), 32).to_s
|
|
190
|
+
# #=> "-0.99999999999999999999999999999997e0"
|
|
100
191
|
#
|
|
101
192
|
def cos(x, prec)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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'),
|
|
143
|
-
# #=> "-0.
|
|
278
|
+
# BigMath.atan(BigDecimal('-1'), 32).to_s
|
|
279
|
+
# #=> "-0.78539816339744830961566084581988e0"
|
|
144
280
|
#
|
|
145
281
|
def atan(x, prec)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
152
|
-
x = BigDecimal("1").div(x,
|
|
153
|
-
x = (-1 + sqrt(1 + x
|
|
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(
|
|
180
|
-
# #=> "0.
|
|
897
|
+
# BigMath.PI(32).to_s
|
|
898
|
+
# #=> "0.31415926535897932384626433832795e1"
|
|
181
899
|
#
|
|
182
900
|
def PI(prec)
|
|
183
|
-
|
|
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(
|
|
225
|
-
# #=> "0.
|
|
942
|
+
# BigMath.E(32).to_s
|
|
943
|
+
# #=> "0.27182818284590452353602874713527e1"
|
|
226
944
|
#
|
|
227
945
|
def E(prec)
|
|
228
|
-
|
|
229
|
-
|
|
946
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :E)
|
|
947
|
+
exp(1, prec)
|
|
230
948
|
end
|
|
231
949
|
end
|