bigdecimal 3.3.1 → 4.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bigdecimal.gemspec +8 -1
- data/ext/bigdecimal/bigdecimal.c +288 -303
- data/ext/bigdecimal/bigdecimal.h +48 -42
- data/ext/bigdecimal/div.h +219 -0
- data/ext/bigdecimal/extconf.rb +5 -2
- data/ext/bigdecimal/missing/dtoa.c +184 -137
- data/ext/bigdecimal/missing.h +4 -2
- 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/erf.rb +291 -0
- data/lib/bigdecimal/math/gamma.rb +513 -0
- data/lib/bigdecimal/math.rb +548 -81
- data/lib/bigdecimal/newton.rb +2 -0
- data/lib/bigdecimal/util.rb +1 -1
- data/lib/bigdecimal.rb +121 -73
- data/sample/linear.rb +73 -37
- data/sample/nlsolve.rb +47 -30
- data/sample/pi.rb +2 -7
- data/sig/big_decimal.rbs +1504 -0
- data/sig/big_decimal_util.rbs +158 -0
- data/sig/big_math.rbs +423 -0
- metadata +10 -3
data/lib/bigdecimal/math.rb
CHANGED
|
@@ -1,19 +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)
|
|
10
12
|
# tan (x, prec)
|
|
13
|
+
# asin(x, prec)
|
|
14
|
+
# acos(x, prec)
|
|
11
15
|
# atan(x, prec)
|
|
16
|
+
# atan2(y, x, prec)
|
|
17
|
+
# sinh (x, prec)
|
|
18
|
+
# cosh (x, prec)
|
|
19
|
+
# tanh (x, prec)
|
|
20
|
+
# asinh(x, prec)
|
|
21
|
+
# acosh(x, prec)
|
|
22
|
+
# atanh(x, prec)
|
|
23
|
+
# log2 (x, prec)
|
|
24
|
+
# log10(x, prec)
|
|
25
|
+
# log1p(x, prec)
|
|
26
|
+
# expm1(x, prec)
|
|
27
|
+
# erf (x, prec)
|
|
28
|
+
# erfc(x, prec)
|
|
29
|
+
# gamma(x, prec)
|
|
30
|
+
# lgamma(x, prec)
|
|
31
|
+
# frexp(x)
|
|
32
|
+
# ldexp(x, exponent)
|
|
12
33
|
# PI (prec)
|
|
13
34
|
# E (prec) == exp(1.0,prec)
|
|
14
35
|
#
|
|
15
36
|
# where:
|
|
16
|
-
# x
|
|
37
|
+
# x, y ... BigDecimal number to be computed.
|
|
17
38
|
# prec ... Number of digits to be obtained.
|
|
18
39
|
#++
|
|
19
40
|
#
|
|
@@ -54,8 +75,8 @@ module BigMath
|
|
|
54
75
|
private_class_method def _sin_periodic_reduction(x, prec, add_half_pi: false) # :nodoc:
|
|
55
76
|
return [1, x] if -Math::PI/2 <= x && x <= Math::PI/2 && !add_half_pi
|
|
56
77
|
|
|
57
|
-
mod_prec = prec + BigDecimal
|
|
58
|
-
pi_extra_prec = [x.exponent, 0].max + BigDecimal
|
|
78
|
+
mod_prec = prec + BigDecimal::Internal::EXTRA_PREC
|
|
79
|
+
pi_extra_prec = [x.exponent, 0].max + BigDecimal::Internal::EXTRA_PREC
|
|
59
80
|
while true
|
|
60
81
|
pi = PI(mod_prec + pi_extra_prec)
|
|
61
82
|
half_pi = pi / 2
|
|
@@ -63,16 +84,92 @@ module BigMath
|
|
|
63
84
|
mod -= half_pi
|
|
64
85
|
if mod.zero? || mod_prec + mod.exponent <= 0
|
|
65
86
|
# mod is too small to estimate required pi precision
|
|
66
|
-
mod_prec = mod_prec * 3 / 2 + BigDecimal
|
|
87
|
+
mod_prec = mod_prec * 3 / 2 + BigDecimal::Internal::EXTRA_PREC
|
|
67
88
|
elsif mod_prec + mod.exponent < prec
|
|
68
89
|
# Estimate required precision of pi
|
|
69
|
-
mod_prec = prec - mod.exponent + BigDecimal
|
|
90
|
+
mod_prec = prec - mod.exponent + BigDecimal::Internal::EXTRA_PREC
|
|
70
91
|
else
|
|
71
92
|
return [div % 2 == 0 ? 1 : -1, mod.mult(1, prec)]
|
|
72
93
|
end
|
|
73
94
|
end
|
|
74
95
|
end
|
|
75
96
|
|
|
97
|
+
private_class_method def _sin_binary_splitting(x, prec) # :nodoc:
|
|
98
|
+
return x if x.zero?
|
|
99
|
+
x2 = x.mult(x, prec)
|
|
100
|
+
# Find k that satisfies x2**k / (2k+1)! < 10**(-prec)
|
|
101
|
+
log10 = Math.log(10)
|
|
102
|
+
logx = BigDecimal::Internal.float_log(x.abs)
|
|
103
|
+
step = (1..).bsearch { |k| Math.lgamma(2 * k + 1)[0] - 2 * k * logx > prec * log10 }
|
|
104
|
+
# Construct denominator sequence for binary splitting
|
|
105
|
+
# sin(x) = x*(1-x2/(2*3)*(1-x2/(4*5)*(1-x2/(6*7)*(1-x2/(8*9)*(1-...)))))
|
|
106
|
+
ds = (1..step).map {|i| -(2 * i) * (2 * i + 1) }
|
|
107
|
+
x.mult(1 + BigDecimal::Internal.taylor_sum_binary_splitting(x2, ds, prec), prec)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private_class_method def _sin_around_zero(x, prec) # :nodoc:
|
|
111
|
+
# Divide x into several parts
|
|
112
|
+
# sin(x.xxxxxxxx...) = sin(x.xx + 0.00xx + 0.0000xxxx + ...)
|
|
113
|
+
# Calculate sin of each part and restore sin(0.xxxxxxxx...) using addition theorem.
|
|
114
|
+
sin = BigDecimal(0)
|
|
115
|
+
cos = BigDecimal(1)
|
|
116
|
+
n = 2
|
|
117
|
+
while x != 0 do
|
|
118
|
+
partial_x = x.truncate(n)
|
|
119
|
+
x -= partial_x
|
|
120
|
+
s = _sin_binary_splitting(partial_x, prec)
|
|
121
|
+
c = (1 - s * s).sqrt(prec)
|
|
122
|
+
sin, cos = (sin * c).add(cos * s, prec), (cos * c).sub(sin * s, prec)
|
|
123
|
+
n *= 2
|
|
124
|
+
end
|
|
125
|
+
sin.clamp(BigDecimal(-1), BigDecimal(1))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# call-seq:
|
|
129
|
+
# cbrt(decimal, numeric) -> BigDecimal
|
|
130
|
+
#
|
|
131
|
+
# Computes the cube root of +decimal+ to the specified number of digits of
|
|
132
|
+
# precision, +numeric+.
|
|
133
|
+
#
|
|
134
|
+
# BigMath.cbrt(BigDecimal('2'), 32).to_s
|
|
135
|
+
# #=> "0.12599210498948731647672106072782e1"
|
|
136
|
+
#
|
|
137
|
+
def cbrt(x, prec)
|
|
138
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :cbrt)
|
|
139
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cbrt)
|
|
140
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
141
|
+
return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
|
|
142
|
+
return BigDecimal(0) if x.zero?
|
|
143
|
+
|
|
144
|
+
x = -x if neg = x < 0
|
|
145
|
+
ex = x.exponent / 3
|
|
146
|
+
x = x._decimal_shift(-3 * ex)
|
|
147
|
+
y = BigDecimal(Math.cbrt(x.to_f), 0)
|
|
148
|
+
BigDecimal::Internal.newton_loop(prec + BigDecimal::Internal::EXTRA_PREC) do |p|
|
|
149
|
+
y = (2 * y + x.div(y, p).div(y, p)).div(3, p)
|
|
150
|
+
end
|
|
151
|
+
y._decimal_shift(ex).mult(neg ? -1 : 1, prec)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# call-seq:
|
|
155
|
+
# hypot(x, y, numeric) -> BigDecimal
|
|
156
|
+
#
|
|
157
|
+
# Returns sqrt(x**2 + y**2) to the specified number of digits of
|
|
158
|
+
# precision, +numeric+.
|
|
159
|
+
#
|
|
160
|
+
# BigMath.hypot(BigDecimal('1'), BigDecimal('2'), 32).to_s
|
|
161
|
+
# #=> "0.22360679774997896964091736687313e1"
|
|
162
|
+
#
|
|
163
|
+
def hypot(x, y, prec)
|
|
164
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :hypot)
|
|
165
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :hypot)
|
|
166
|
+
y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :hypot)
|
|
167
|
+
return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan?
|
|
168
|
+
return BigDecimal::Internal.infinity_computation_result if x.infinite? || y.infinite?
|
|
169
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
170
|
+
sqrt(x.mult(x, prec2) + y.mult(y, prec2), prec)
|
|
171
|
+
end
|
|
172
|
+
|
|
76
173
|
# call-seq:
|
|
77
174
|
# sin(decimal, numeric) -> BigDecimal
|
|
78
175
|
#
|
|
@@ -88,26 +185,9 @@ module BigMath
|
|
|
88
185
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :sin)
|
|
89
186
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sin)
|
|
90
187
|
return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
|
|
91
|
-
n
|
|
92
|
-
one = BigDecimal("1")
|
|
93
|
-
two = BigDecimal("2")
|
|
188
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
94
189
|
sign, x = _sin_periodic_reduction(x, n)
|
|
95
|
-
|
|
96
|
-
x2 = x.mult(x,n)
|
|
97
|
-
y = x
|
|
98
|
-
d = y
|
|
99
|
-
i = one
|
|
100
|
-
z = one
|
|
101
|
-
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
|
102
|
-
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
|
103
|
-
x1 = -x2.mult(x1,n)
|
|
104
|
-
i += two
|
|
105
|
-
z *= (i-one) * i
|
|
106
|
-
d = x1.div(z,m)
|
|
107
|
-
y += d
|
|
108
|
-
end
|
|
109
|
-
y = BigDecimal("1") if y > 1
|
|
110
|
-
y.mult(sign, prec)
|
|
190
|
+
_sin_around_zero(x, n).mult(sign, prec)
|
|
111
191
|
end
|
|
112
192
|
|
|
113
193
|
# call-seq:
|
|
@@ -125,8 +205,9 @@ module BigMath
|
|
|
125
205
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :cos)
|
|
126
206
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cos)
|
|
127
207
|
return BigDecimal::Internal.nan_computation_result if x.infinite? || x.nan?
|
|
128
|
-
|
|
129
|
-
sign
|
|
208
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
209
|
+
sign, x = _sin_periodic_reduction(x, n, add_half_pi: true)
|
|
210
|
+
_sin_around_zero(x, n).mult(sign, prec)
|
|
130
211
|
end
|
|
131
212
|
|
|
132
213
|
# call-seq:
|
|
@@ -145,7 +226,59 @@ module BigMath
|
|
|
145
226
|
#
|
|
146
227
|
def tan(x, prec)
|
|
147
228
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :tan)
|
|
148
|
-
|
|
229
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
230
|
+
sin(x, prec2).div(cos(x, prec2), prec)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# call-seq:
|
|
234
|
+
# asin(decimal, numeric) -> BigDecimal
|
|
235
|
+
#
|
|
236
|
+
# Computes the arcsine of +decimal+ to the specified number of digits of
|
|
237
|
+
# precision, +numeric+.
|
|
238
|
+
#
|
|
239
|
+
# If +decimal+ is NaN, returns NaN.
|
|
240
|
+
#
|
|
241
|
+
# BigMath.asin(BigDecimal('0.5'), 32).to_s
|
|
242
|
+
# #=> "0.52359877559829887307710723054658e0"
|
|
243
|
+
#
|
|
244
|
+
def asin(x, prec)
|
|
245
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :asin)
|
|
246
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asin)
|
|
247
|
+
raise Math::DomainError, "Out of domain argument for asin" if x < -1 || x > 1
|
|
248
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
249
|
+
|
|
250
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
251
|
+
cos = (1 - x**2).sqrt(prec2)
|
|
252
|
+
if cos.zero?
|
|
253
|
+
PI(prec2).div(x > 0 ? 2 : -2, prec)
|
|
254
|
+
else
|
|
255
|
+
atan(x.div(cos, prec2), prec)
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
# call-seq:
|
|
260
|
+
# acos(decimal, numeric) -> BigDecimal
|
|
261
|
+
#
|
|
262
|
+
# Computes the arccosine of +decimal+ to the specified number of digits of
|
|
263
|
+
# precision, +numeric+.
|
|
264
|
+
#
|
|
265
|
+
# If +decimal+ is NaN, returns NaN.
|
|
266
|
+
#
|
|
267
|
+
# BigMath.acos(BigDecimal('0.5'), 32).to_s
|
|
268
|
+
# #=> "0.10471975511965977461542144610932e1"
|
|
269
|
+
#
|
|
270
|
+
def acos(x, prec)
|
|
271
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :acos)
|
|
272
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acos)
|
|
273
|
+
raise Math::DomainError, "Out of domain argument for acos" if x < -1 || x > 1
|
|
274
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
275
|
+
|
|
276
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
277
|
+
return (PI(prec2) / 2).sub(asin(x, prec2), prec) if x < 0
|
|
278
|
+
return PI(prec2).div(2, prec) if x.zero?
|
|
279
|
+
|
|
280
|
+
sin = (1 - x**2).sqrt(prec2)
|
|
281
|
+
atan(sin.div(x, prec2), prec)
|
|
149
282
|
end
|
|
150
283
|
|
|
151
284
|
# call-seq:
|
|
@@ -163,29 +296,382 @@ module BigMath
|
|
|
163
296
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan)
|
|
164
297
|
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan)
|
|
165
298
|
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
166
|
-
n = prec + BigDecimal
|
|
167
|
-
|
|
299
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
300
|
+
return PI(n).div(2 * x.infinite?, prec) if x.infinite?
|
|
301
|
+
|
|
168
302
|
x = -x if neg = x < 0
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
y = x
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
303
|
+
x = BigDecimal(1).div(x, n) if inv = x < -1 || x > 1
|
|
304
|
+
|
|
305
|
+
# Solve tan(y) - x = 0 with Newton's method
|
|
306
|
+
# Repeat: y -= (tan(y) - x) * cos(y)**2
|
|
307
|
+
y = BigDecimal(Math.atan(x.to_f), 0)
|
|
308
|
+
BigDecimal::Internal.newton_loop(n) do |p|
|
|
309
|
+
s = sin(y, p)
|
|
310
|
+
c = (1 - s * s).sqrt(p)
|
|
311
|
+
y = y.sub(c * (s.sub(c * x.mult(1, p), p)), p)
|
|
312
|
+
end
|
|
313
|
+
y = PI(n) / 2 - y if inv
|
|
314
|
+
y.mult(neg ? -1 : 1, prec)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# call-seq:
|
|
318
|
+
# atan2(decimal, decimal, numeric) -> BigDecimal
|
|
319
|
+
#
|
|
320
|
+
# Computes the arctangent of y and x to the specified number of digits of
|
|
321
|
+
# precision, +numeric+.
|
|
322
|
+
#
|
|
323
|
+
# BigMath.atan2(BigDecimal('-1'), BigDecimal('1'), 32).to_s
|
|
324
|
+
# #=> "-0.78539816339744830961566084581988e0"
|
|
325
|
+
#
|
|
326
|
+
def atan2(y, x, prec)
|
|
327
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :atan2)
|
|
328
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atan2)
|
|
329
|
+
y = BigDecimal::Internal.coerce_to_bigdecimal(y, prec, :atan2)
|
|
330
|
+
return BigDecimal::Internal.nan_computation_result if x.nan? || y.nan?
|
|
331
|
+
|
|
332
|
+
if x.infinite? || y.infinite?
|
|
333
|
+
one = BigDecimal(1)
|
|
334
|
+
zero = BigDecimal(0)
|
|
335
|
+
x = x.infinite? ? (x > 0 ? one : -one) : zero
|
|
336
|
+
y = y.infinite? ? (y > 0 ? one : -one) : y.sign * zero
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
return x.sign >= 0 ? BigDecimal(0) : y.sign * PI(prec) if y.zero?
|
|
340
|
+
|
|
341
|
+
y = -y if neg = y < 0
|
|
342
|
+
xlarge = y.abs < x.abs
|
|
343
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
344
|
+
if x > 0
|
|
345
|
+
v = xlarge ? atan(y.div(x, prec2), prec) : PI(prec2) / 2 - atan(x.div(y, prec2), prec2)
|
|
346
|
+
else
|
|
347
|
+
v = xlarge ? PI(prec2) - atan(-y.div(x, prec2), prec2) : PI(prec2) / 2 + atan(x.div(-y, prec2), prec2)
|
|
348
|
+
end
|
|
349
|
+
v.mult(neg ? -1 : 1, prec)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# call-seq:
|
|
353
|
+
# sinh(decimal, numeric) -> BigDecimal
|
|
354
|
+
#
|
|
355
|
+
# Computes the hyperbolic sine of +decimal+ to the specified number of digits of
|
|
356
|
+
# precision, +numeric+.
|
|
357
|
+
#
|
|
358
|
+
# If +decimal+ is NaN, returns NaN.
|
|
359
|
+
#
|
|
360
|
+
# BigMath.sinh(BigDecimal('1'), 32).to_s
|
|
361
|
+
# #=> "0.11752011936438014568823818505956e1"
|
|
362
|
+
#
|
|
363
|
+
def sinh(x, prec)
|
|
364
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :sinh)
|
|
365
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :sinh)
|
|
366
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
367
|
+
return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
|
|
368
|
+
|
|
369
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
370
|
+
prec2 -= x.exponent if x.exponent < 0
|
|
371
|
+
e = exp(x, prec2)
|
|
372
|
+
(e - BigDecimal(1).div(e, prec2)).div(2, prec)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# call-seq:
|
|
376
|
+
# cosh(decimal, numeric) -> BigDecimal
|
|
377
|
+
#
|
|
378
|
+
# Computes the hyperbolic cosine of +decimal+ to the specified number of digits of
|
|
379
|
+
# precision, +numeric+.
|
|
380
|
+
#
|
|
381
|
+
# If +decimal+ is NaN, returns NaN.
|
|
382
|
+
#
|
|
383
|
+
# BigMath.cosh(BigDecimal('1'), 32).to_s
|
|
384
|
+
# #=> "0.15430806348152437784779056207571e1"
|
|
385
|
+
#
|
|
386
|
+
def cosh(x, prec)
|
|
387
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :cosh)
|
|
388
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :cosh)
|
|
389
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
390
|
+
return BigDecimal::Internal.infinity_computation_result if x.infinite?
|
|
391
|
+
|
|
392
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
393
|
+
e = exp(x, prec2)
|
|
394
|
+
(e + BigDecimal(1).div(e, prec2)).div(2, prec)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# call-seq:
|
|
398
|
+
# tanh(decimal, numeric) -> BigDecimal
|
|
399
|
+
#
|
|
400
|
+
# Computes the hyperbolic tangent of +decimal+ to the specified number of digits of
|
|
401
|
+
# precision, +numeric+.
|
|
402
|
+
#
|
|
403
|
+
# If +decimal+ is NaN, returns NaN.
|
|
404
|
+
#
|
|
405
|
+
# BigMath.tanh(BigDecimal('1'), 32).to_s
|
|
406
|
+
# #=> "0.76159415595576488811945828260479e0"
|
|
407
|
+
#
|
|
408
|
+
def tanh(x, prec)
|
|
409
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :tanh)
|
|
410
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :tanh)
|
|
411
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
412
|
+
return BigDecimal(x.infinite?) if x.infinite?
|
|
413
|
+
|
|
414
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC + [-x.exponent, 0].max
|
|
415
|
+
e = exp(x, prec2)
|
|
416
|
+
einv = BigDecimal(1).div(e, prec2)
|
|
417
|
+
(e - einv).div(e + einv, prec)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# call-seq:
|
|
421
|
+
# asinh(decimal, numeric) -> BigDecimal
|
|
422
|
+
#
|
|
423
|
+
# Computes the inverse hyperbolic sine of +decimal+ to the specified number of digits of
|
|
424
|
+
# precision, +numeric+.
|
|
425
|
+
#
|
|
426
|
+
# If +decimal+ is NaN, returns NaN.
|
|
427
|
+
#
|
|
428
|
+
# BigMath.asinh(BigDecimal('1'), 32).to_s
|
|
429
|
+
# #=> "0.88137358701954302523260932497979e0"
|
|
430
|
+
#
|
|
431
|
+
def asinh(x, prec)
|
|
432
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :asinh)
|
|
433
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :asinh)
|
|
434
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
435
|
+
return BigDecimal::Internal.infinity_computation_result * x.infinite? if x.infinite?
|
|
436
|
+
return -asinh(-x, prec) if x < 0
|
|
437
|
+
|
|
438
|
+
sqrt_prec = prec + [-x.exponent, 0].max + BigDecimal::Internal::EXTRA_PREC
|
|
439
|
+
log(x + sqrt(x**2 + 1, sqrt_prec), prec)
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# call-seq:
|
|
443
|
+
# acosh(decimal, numeric) -> BigDecimal
|
|
444
|
+
#
|
|
445
|
+
# Computes the inverse hyperbolic cosine of +decimal+ to the specified number of digits of
|
|
446
|
+
# precision, +numeric+.
|
|
447
|
+
#
|
|
448
|
+
# If +decimal+ is NaN, returns NaN.
|
|
449
|
+
#
|
|
450
|
+
# BigMath.acosh(BigDecimal('2'), 32).to_s
|
|
451
|
+
# #=> "0.1316957896924816708625046347308e1"
|
|
452
|
+
#
|
|
453
|
+
def acosh(x, prec)
|
|
454
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :acosh)
|
|
455
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :acosh)
|
|
456
|
+
raise Math::DomainError, "Out of domain argument for acosh" if x < 1
|
|
457
|
+
return BigDecimal::Internal.infinity_computation_result if x.infinite?
|
|
458
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
459
|
+
|
|
460
|
+
log(x + sqrt(x**2 - 1, prec + BigDecimal::Internal::EXTRA_PREC), prec)
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# call-seq:
|
|
464
|
+
# atanh(decimal, numeric) -> BigDecimal
|
|
465
|
+
#
|
|
466
|
+
# Computes the inverse hyperbolic tangent of +decimal+ to the specified number of digits of
|
|
467
|
+
# precision, +numeric+.
|
|
468
|
+
#
|
|
469
|
+
# If +decimal+ is NaN, returns NaN.
|
|
470
|
+
#
|
|
471
|
+
# BigMath.atanh(BigDecimal('0.5'), 32).to_s
|
|
472
|
+
# #=> "0.54930614433405484569762261846126e0"
|
|
473
|
+
#
|
|
474
|
+
def atanh(x, prec)
|
|
475
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :atanh)
|
|
476
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :atanh)
|
|
477
|
+
raise Math::DomainError, "Out of domain argument for atanh" if x < -1 || x > 1
|
|
478
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
479
|
+
return BigDecimal::Internal.infinity_computation_result if x == 1
|
|
480
|
+
return -BigDecimal::Internal.infinity_computation_result if x == -1
|
|
481
|
+
|
|
482
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC
|
|
483
|
+
(log(x + 1, prec2) - log(1 - x, prec2)).div(2, prec)
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
# call-seq:
|
|
487
|
+
# BigMath.log2(decimal, numeric) -> BigDecimal
|
|
488
|
+
#
|
|
489
|
+
# Computes the base 2 logarithm of +decimal+ to the specified number of
|
|
490
|
+
# digits of precision, +numeric+.
|
|
491
|
+
#
|
|
492
|
+
# If +decimal+ is zero or negative, raises Math::DomainError.
|
|
493
|
+
#
|
|
494
|
+
# If +decimal+ is positive infinity, returns Infinity.
|
|
495
|
+
#
|
|
496
|
+
# If +decimal+ is NaN, returns NaN.
|
|
497
|
+
#
|
|
498
|
+
# BigMath.log2(BigDecimal('3'), 32).to_s
|
|
499
|
+
# #=> "0.15849625007211561814537389439478e1"
|
|
500
|
+
#
|
|
501
|
+
def log2(x, prec)
|
|
502
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :log2)
|
|
503
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log2)
|
|
504
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
505
|
+
return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
|
|
506
|
+
|
|
507
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC * 3 / 2
|
|
508
|
+
v = log(x, prec2).div(log(BigDecimal(2), prec2), prec2)
|
|
509
|
+
# Perform half-up rounding to calculate log2(2**n)==n correctly in every rounding mode
|
|
510
|
+
v = v.round(prec + BigDecimal::Internal::EXTRA_PREC - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
|
|
511
|
+
v.mult(1, prec)
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# call-seq:
|
|
515
|
+
# BigMath.log10(decimal, numeric) -> BigDecimal
|
|
516
|
+
#
|
|
517
|
+
# Computes the base 10 logarithm of +decimal+ to the specified number of
|
|
518
|
+
# digits of precision, +numeric+.
|
|
519
|
+
#
|
|
520
|
+
# If +decimal+ is zero or negative, raises Math::DomainError.
|
|
521
|
+
#
|
|
522
|
+
# If +decimal+ is positive infinity, returns Infinity.
|
|
523
|
+
#
|
|
524
|
+
# If +decimal+ is NaN, returns NaN.
|
|
525
|
+
#
|
|
526
|
+
# BigMath.log10(BigDecimal('3'), 32).to_s
|
|
527
|
+
# #=> "0.47712125471966243729502790325512e0"
|
|
528
|
+
#
|
|
529
|
+
def log10(x, prec)
|
|
530
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :log10)
|
|
531
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log10)
|
|
532
|
+
return BigDecimal::Internal.nan_computation_result if x.nan?
|
|
533
|
+
return BigDecimal::Internal.infinity_computation_result if x.infinite? == 1
|
|
534
|
+
|
|
535
|
+
prec2 = prec + BigDecimal::Internal::EXTRA_PREC * 3 / 2
|
|
536
|
+
v = log(x, prec2).div(log(BigDecimal(10), prec2), prec2)
|
|
537
|
+
# Perform half-up rounding to calculate log10(10**n)==n correctly in every rounding mode
|
|
538
|
+
v = v.round(prec + BigDecimal::Internal::EXTRA_PREC - (v.exponent < 0 ? v.exponent : 0), BigDecimal::ROUND_HALF_UP)
|
|
539
|
+
v.mult(1, prec)
|
|
540
|
+
end
|
|
541
|
+
|
|
542
|
+
# call-seq:
|
|
543
|
+
# BigMath.log1p(decimal, numeric) -> BigDecimal
|
|
544
|
+
#
|
|
545
|
+
# Computes log(1 + decimal) to the specified number of digits of precision, +numeric+.
|
|
546
|
+
#
|
|
547
|
+
# BigMath.log1p(BigDecimal('0.1'), 32).to_s
|
|
548
|
+
# #=> "0.95310179804324860043952123280765e-1"
|
|
549
|
+
#
|
|
550
|
+
def log1p(x, prec)
|
|
551
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :log1p)
|
|
552
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :log1p)
|
|
553
|
+
raise Math::DomainError, 'Out of domain argument for log1p' if x < -1
|
|
554
|
+
|
|
555
|
+
return log(x + 1, prec)
|
|
556
|
+
end
|
|
557
|
+
|
|
558
|
+
# call-seq:
|
|
559
|
+
# BigMath.expm1(decimal, numeric) -> BigDecimal
|
|
560
|
+
#
|
|
561
|
+
# Computes exp(decimal) - 1 to the specified number of digits of precision, +numeric+.
|
|
562
|
+
#
|
|
563
|
+
# BigMath.expm1(BigDecimal('0.1'), 32).to_s
|
|
564
|
+
# #=> "0.10517091807564762481170782649025e0"
|
|
565
|
+
#
|
|
566
|
+
def expm1(x, prec)
|
|
567
|
+
prec = BigDecimal::Internal.coerce_validate_prec(prec, :expm1)
|
|
568
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, prec, :expm1)
|
|
569
|
+
return BigDecimal(-1) if x.infinite? == -1
|
|
570
|
+
|
|
571
|
+
exp_prec = prec
|
|
572
|
+
if x < -1
|
|
573
|
+
# log10(exp(x)) = x * log10(e)
|
|
574
|
+
lg_e = 0.4342944819032518
|
|
575
|
+
exp_prec = prec + (lg_e * x).ceil + BigDecimal::Internal::EXTRA_PREC
|
|
576
|
+
elsif x < 1
|
|
577
|
+
exp_prec = prec - x.exponent + BigDecimal::Internal::EXTRA_PREC
|
|
578
|
+
else
|
|
579
|
+
exp_prec = prec
|
|
184
580
|
end
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
581
|
+
|
|
582
|
+
return BigDecimal(-1) if exp_prec <= 0
|
|
583
|
+
|
|
584
|
+
exp(x, exp_prec).sub(1, prec)
|
|
585
|
+
end
|
|
586
|
+
|
|
587
|
+
# call-seq:
|
|
588
|
+
# erf(decimal, numeric) -> BigDecimal
|
|
589
|
+
#
|
|
590
|
+
# Computes the error function of +decimal+ to the specified number of digits of
|
|
591
|
+
# precision, +numeric+.
|
|
592
|
+
#
|
|
593
|
+
# If +decimal+ is NaN, returns NaN.
|
|
594
|
+
#
|
|
595
|
+
# BigMath.erf(BigDecimal('1'), 32).to_s
|
|
596
|
+
# #=> "0.84270079294971486934122063508261e0"
|
|
597
|
+
#
|
|
598
|
+
def erf(x, prec)
|
|
599
|
+
require 'bigdecimal/math/erf'
|
|
600
|
+
Erf.erf(x, prec)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
# call-seq:
|
|
604
|
+
# erfc(decimal, numeric) -> BigDecimal
|
|
605
|
+
#
|
|
606
|
+
# Computes the complementary error function of +decimal+ to the specified number of digits of
|
|
607
|
+
# precision, +numeric+.
|
|
608
|
+
#
|
|
609
|
+
# If +decimal+ is NaN, returns NaN.
|
|
610
|
+
#
|
|
611
|
+
# BigMath.erfc(BigDecimal('10'), 32).to_s
|
|
612
|
+
# #=> "0.20884875837625447570007862949578e-44"
|
|
613
|
+
#
|
|
614
|
+
def erfc(x, prec)
|
|
615
|
+
require 'bigdecimal/math/erf'
|
|
616
|
+
Erf.erfc(x, prec)
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
# call-seq:
|
|
620
|
+
# BigMath.gamma(decimal, numeric) -> BigDecimal
|
|
621
|
+
#
|
|
622
|
+
# Computes the gamma function of +decimal+ to the specified number of
|
|
623
|
+
# digits of precision, +numeric+.
|
|
624
|
+
#
|
|
625
|
+
# BigMath.gamma(BigDecimal('0.5'), 32).to_s
|
|
626
|
+
# #=> "0.17724538509055160272981674833411e1"
|
|
627
|
+
#
|
|
628
|
+
def gamma(x, prec)
|
|
629
|
+
require 'bigdecimal/math/gamma'
|
|
630
|
+
Gamma.gamma(x, prec)
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
# call-seq:
|
|
634
|
+
# BigMath.lgamma(decimal, numeric) -> [BigDecimal, Integer]
|
|
635
|
+
#
|
|
636
|
+
# Computes the natural logarithm of the absolute value of the gamma function
|
|
637
|
+
# of +decimal+ to the specified number of digits of precision, +numeric+ and its sign.
|
|
638
|
+
#
|
|
639
|
+
# BigMath.lgamma(BigDecimal('0.5'), 32)
|
|
640
|
+
# #=> [0.57236494292470008707171367567653e0, 1]
|
|
641
|
+
#
|
|
642
|
+
def lgamma(x, prec)
|
|
643
|
+
require 'bigdecimal/math/gamma'
|
|
644
|
+
Gamma.lgamma(x, prec)
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
# call-seq:
|
|
648
|
+
# frexp(x) -> [BigDecimal, Integer]
|
|
649
|
+
#
|
|
650
|
+
# Decomposes +x+ into a normalized fraction and an integral power of ten.
|
|
651
|
+
#
|
|
652
|
+
# BigMath.frexp(BigDecimal(123.456))
|
|
653
|
+
# #=> [0.123456e0, 3]
|
|
654
|
+
#
|
|
655
|
+
def frexp(x)
|
|
656
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :frexp)
|
|
657
|
+
return [x, 0] unless x.finite?
|
|
658
|
+
|
|
659
|
+
exponent = x.exponent
|
|
660
|
+
[x._decimal_shift(-exponent), exponent]
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
# call-seq:
|
|
664
|
+
# ldexp(fraction, exponent) -> BigDecimal
|
|
665
|
+
#
|
|
666
|
+
# Inverse of +frexp+.
|
|
667
|
+
# Returns the value of fraction * 10**exponent.
|
|
668
|
+
#
|
|
669
|
+
# BigMath.ldexp(BigDecimal("0.123456e0"), 3)
|
|
670
|
+
# #=> 0.123456e3
|
|
671
|
+
#
|
|
672
|
+
def ldexp(x, exponent)
|
|
673
|
+
x = BigDecimal::Internal.coerce_to_bigdecimal(x, 0, :ldexp)
|
|
674
|
+
x.finite? ? x._decimal_shift(exponent) : x
|
|
189
675
|
end
|
|
190
676
|
|
|
191
677
|
# call-seq:
|
|
@@ -198,39 +684,20 @@ module BigMath
|
|
|
198
684
|
# #=> "0.31415926535897932384626433832795e1"
|
|
199
685
|
#
|
|
200
686
|
def PI(prec)
|
|
687
|
+
# Gauss–Legendre algorithm
|
|
201
688
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :PI)
|
|
202
|
-
n
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
d = one
|
|
213
|
-
k = one
|
|
214
|
-
t = BigDecimal("-80")
|
|
215
|
-
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
|
|
216
|
-
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
|
217
|
-
t = t*m25
|
|
218
|
-
d = t.div(k,m)
|
|
219
|
-
k = k+two
|
|
220
|
-
pi = pi + d
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
d = one
|
|
224
|
-
k = one
|
|
225
|
-
t = BigDecimal("956")
|
|
226
|
-
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
|
|
227
|
-
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
|
228
|
-
t = t.div(m57121,n)
|
|
229
|
-
d = t.div(k,m)
|
|
230
|
-
pi = pi + d
|
|
231
|
-
k = k+two
|
|
689
|
+
n = prec + BigDecimal::Internal::EXTRA_PREC
|
|
690
|
+
a = BigDecimal(1)
|
|
691
|
+
b = BigDecimal(0.5, 0).sqrt(n)
|
|
692
|
+
s = BigDecimal(0.25, 0)
|
|
693
|
+
t = 1
|
|
694
|
+
while a != b && (a - b).exponent > 1 - n
|
|
695
|
+
c = (a - b).div(2, n)
|
|
696
|
+
a, b = (a + b).div(2, n), (a * b).sqrt(n)
|
|
697
|
+
s = s.sub(c * c * t, n)
|
|
698
|
+
t *= 2
|
|
232
699
|
end
|
|
233
|
-
|
|
700
|
+
(a * b).div(s, prec)
|
|
234
701
|
end
|
|
235
702
|
|
|
236
703
|
# call-seq:
|
|
@@ -244,6 +711,6 @@ module BigMath
|
|
|
244
711
|
#
|
|
245
712
|
def E(prec)
|
|
246
713
|
prec = BigDecimal::Internal.coerce_validate_prec(prec, :E)
|
|
247
|
-
|
|
714
|
+
exp(1, prec)
|
|
248
715
|
end
|
|
249
716
|
end
|