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.
- checksums.yaml +4 -4
- data/bigdecimal.gemspec +6 -1
- data/ext/bigdecimal/bigdecimal.c +972 -2552
- data/ext/bigdecimal/bigdecimal.h +45 -60
- data/ext/bigdecimal/bits.h +3 -0
- data/ext/bigdecimal/div.h +192 -0
- data/ext/bigdecimal/extconf.rb +7 -8
- data/ext/bigdecimal/missing.h +5 -95
- data/ext/bigdecimal/ntt.h +191 -0
- data/lib/bigdecimal/jacobian.rb +2 -0
- data/lib/bigdecimal/ludcmp.rb +2 -0
- data/lib/bigdecimal/math.rb +828 -132
- data/lib/bigdecimal/newton.rb +2 -0
- data/lib/bigdecimal/util.rb +16 -15
- data/lib/bigdecimal.rb +391 -0
- data/sample/linear.rb +73 -37
- data/sample/nlsolve.rb +47 -30
- data/sample/pi.rb +2 -7
- data/sig/big_decimal.rbs +1502 -0
- data/sig/big_decimal_util.rbs +158 -0
- data/sig/big_math.rbs +423 -0
- metadata +8 -3
data/lib/bigdecimal/math.rb
CHANGED
|
@@ -1,18 +1,40 @@
|
|
|
1
|
-
# frozen_string_literal:
|
|
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
|
|
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,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'),
|
|
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::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,
|
|
55
|
-
# #=> "0.
|
|
181
|
+
# BigMath.sin(BigMath.PI(5)/4, 32).to_s
|
|
182
|
+
# #=> "0.70710807985947359435812921837984e0"
|
|
56
183
|
#
|
|
57
184
|
def sin(x, prec)
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
x
|
|
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(
|
|
99
|
-
# #=> "-0.
|
|
201
|
+
# BigMath.cos(BigMath.PI(16), 32).to_s
|
|
202
|
+
# #=> "-0.99999999999999999999999999999997e0"
|
|
100
203
|
#
|
|
101
204
|
def cos(x, prec)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
x
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
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'),
|
|
143
|
-
# #=> "-0.
|
|
292
|
+
# BigMath.atan(BigDecimal('-1'), 32).to_s
|
|
293
|
+
# #=> "-0.78539816339744830961566084581988e0"
|
|
144
294
|
#
|
|
145
295
|
def atan(x, prec)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
#
|
|
318
|
+
# atan2(decimal, decimal, numeric) -> BigDecimal
|
|
175
319
|
#
|
|
176
|
-
# Computes the
|
|
177
|
-
# +numeric+.
|
|
320
|
+
# Computes the arctangent of y and x to the specified number of digits of
|
|
321
|
+
# precision, +numeric+.
|
|
178
322
|
#
|
|
179
|
-
# BigMath.
|
|
180
|
-
# #=> "0.
|
|
323
|
+
# BigMath.atan2(BigDecimal('-1'), BigDecimal('1'), 32).to_s
|
|
324
|
+
# #=> "-0.78539816339744830961566084581988e0"
|
|
181
325
|
#
|
|
182
|
-
def
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
807
|
+
# Estimate required a for given precision
|
|
808
|
+
a = (prec / Math.log10(2 * Math::PI)).ceil
|
|
193
809
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
k
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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(
|
|
225
|
-
# #=> "0.
|
|
920
|
+
# BigMath.E(32).to_s
|
|
921
|
+
# #=> "0.27182818284590452353602874713527e1"
|
|
226
922
|
#
|
|
227
923
|
def E(prec)
|
|
228
|
-
|
|
229
|
-
|
|
924
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :E)
|
|
925
|
+
exp(1, prec)
|
|
230
926
|
end
|
|
231
927
|
end
|