bigdecimal 2.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 +7 -0
- data/bigdecimal.gemspec +39 -0
- data/ext/bigdecimal/bigdecimal.c +6617 -0
- data/ext/bigdecimal/bigdecimal.h +396 -0
- data/ext/bigdecimal/extconf.rb +55 -0
- data/lib/bigdecimal.rb +1 -0
- data/lib/bigdecimal/jacobian.rb +90 -0
- data/lib/bigdecimal/ludcmp.rb +89 -0
- data/lib/bigdecimal/math.rb +232 -0
- data/lib/bigdecimal/newton.rb +80 -0
- data/lib/bigdecimal/util.rb +181 -0
- data/sample/linear.rb +74 -0
- data/sample/nlsolve.rb +40 -0
- data/sample/pi.rb +21 -0
- metadata +116 -0
data/lib/bigdecimal.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bigdecimal.so'
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
|
3
|
+
require 'bigdecimal'
|
4
|
+
|
5
|
+
# require 'bigdecimal/jacobian'
|
6
|
+
#
|
7
|
+
# Provides methods to compute the Jacobian matrix of a set of equations at a
|
8
|
+
# point x. In the methods below:
|
9
|
+
#
|
10
|
+
# f is an Object which is used to compute the Jacobian matrix of the equations.
|
11
|
+
# It must provide the following methods:
|
12
|
+
#
|
13
|
+
# f.values(x):: returns the values of all functions at x
|
14
|
+
#
|
15
|
+
# f.zero:: returns 0.0
|
16
|
+
# f.one:: returns 1.0
|
17
|
+
# f.two:: returns 2.0
|
18
|
+
# f.ten:: returns 10.0
|
19
|
+
#
|
20
|
+
# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
|
21
|
+
#
|
22
|
+
# x is the point at which to compute the Jacobian.
|
23
|
+
#
|
24
|
+
# fx is f.values(x).
|
25
|
+
#
|
26
|
+
module Jacobian
|
27
|
+
module_function
|
28
|
+
|
29
|
+
# Determines the equality of two numbers by comparing to zero, or using the epsilon value
|
30
|
+
def isEqual(a,b,zero=0.0,e=1.0e-8)
|
31
|
+
aa = a.abs
|
32
|
+
bb = b.abs
|
33
|
+
if aa == zero && bb == zero then
|
34
|
+
true
|
35
|
+
else
|
36
|
+
if ((a-b)/(aa+bb)).abs < e then
|
37
|
+
true
|
38
|
+
else
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
# Computes the derivative of f[i] at x[i].
|
46
|
+
# fx is the value of f at x.
|
47
|
+
def dfdxi(f,fx,x,i)
|
48
|
+
nRetry = 0
|
49
|
+
n = x.size
|
50
|
+
xSave = x[i]
|
51
|
+
ok = 0
|
52
|
+
ratio = f.ten*f.ten*f.ten
|
53
|
+
dx = x[i].abs/ratio
|
54
|
+
dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps)
|
55
|
+
dx = f.one/f.ten if isEqual(dx,f.zero,f.zero,f.eps)
|
56
|
+
until ok>0 do
|
57
|
+
deriv = []
|
58
|
+
nRetry += 1
|
59
|
+
if nRetry > 100
|
60
|
+
raise "Singular Jacobian matrix. No change at x[" + i.to_s + "]"
|
61
|
+
end
|
62
|
+
dx = dx*f.two
|
63
|
+
x[i] += dx
|
64
|
+
fxNew = f.values(x)
|
65
|
+
for j in 0...n do
|
66
|
+
if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then
|
67
|
+
ok += 1
|
68
|
+
deriv <<= (fxNew[j]-fx[j])/dx
|
69
|
+
else
|
70
|
+
deriv <<= f.zero
|
71
|
+
end
|
72
|
+
end
|
73
|
+
x[i] = xSave
|
74
|
+
end
|
75
|
+
deriv
|
76
|
+
end
|
77
|
+
|
78
|
+
# Computes the Jacobian of f at x. fx is the value of f at x.
|
79
|
+
def jacobian(f,fx,x)
|
80
|
+
n = x.size
|
81
|
+
dfdx = Array.new(n*n)
|
82
|
+
for i in 0...n do
|
83
|
+
df = dfdxi(f,fx,x,i)
|
84
|
+
for j in 0...n do
|
85
|
+
dfdx[j*n+i] = df[j]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
dfdx
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
4
|
+
#
|
5
|
+
# Solves a*x = b for x, using LU decomposition.
|
6
|
+
#
|
7
|
+
module LUSolve
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# Performs LU decomposition of the n by n matrix a.
|
11
|
+
def ludecomp(a,n,zero=0,one=1)
|
12
|
+
prec = BigDecimal.limit(nil)
|
13
|
+
ps = []
|
14
|
+
scales = []
|
15
|
+
for i in 0...n do # pick up largest(abs. val.) element in each row.
|
16
|
+
ps <<= i
|
17
|
+
nrmrow = zero
|
18
|
+
ixn = i*n
|
19
|
+
for j in 0...n do
|
20
|
+
biggst = a[ixn+j].abs
|
21
|
+
nrmrow = biggst if biggst>nrmrow
|
22
|
+
end
|
23
|
+
if nrmrow>zero then
|
24
|
+
scales <<= one.div(nrmrow,prec)
|
25
|
+
else
|
26
|
+
raise "Singular matrix"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
n1 = n - 1
|
30
|
+
for k in 0...n1 do # Gaussian elimination with partial pivoting.
|
31
|
+
biggst = zero;
|
32
|
+
for i in k...n do
|
33
|
+
size = a[ps[i]*n+k].abs*scales[ps[i]]
|
34
|
+
if size>biggst then
|
35
|
+
biggst = size
|
36
|
+
pividx = i
|
37
|
+
end
|
38
|
+
end
|
39
|
+
raise "Singular matrix" if biggst<=zero
|
40
|
+
if pividx!=k then
|
41
|
+
j = ps[k]
|
42
|
+
ps[k] = ps[pividx]
|
43
|
+
ps[pividx] = j
|
44
|
+
end
|
45
|
+
pivot = a[ps[k]*n+k]
|
46
|
+
for i in (k+1)...n do
|
47
|
+
psin = ps[i]*n
|
48
|
+
a[psin+k] = mult = a[psin+k].div(pivot,prec)
|
49
|
+
if mult!=zero then
|
50
|
+
pskn = ps[k]*n
|
51
|
+
for j in (k+1)...n do
|
52
|
+
a[psin+j] -= mult.mult(a[pskn+j],prec)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
raise "Singular matrix" if a[ps[n1]*n+n1] == zero
|
58
|
+
ps
|
59
|
+
end
|
60
|
+
|
61
|
+
# Solves a*x = b for x, using LU decomposition.
|
62
|
+
#
|
63
|
+
# a is a matrix, b is a constant vector, x is the solution vector.
|
64
|
+
#
|
65
|
+
# ps is the pivot, a vector which indicates the permutation of rows performed
|
66
|
+
# during LU decomposition.
|
67
|
+
def lusolve(a,b,ps,zero=0.0)
|
68
|
+
prec = BigDecimal.limit(nil)
|
69
|
+
n = ps.size
|
70
|
+
x = []
|
71
|
+
for i in 0...n do
|
72
|
+
dot = zero
|
73
|
+
psin = ps[i]*n
|
74
|
+
for j in 0...i do
|
75
|
+
dot = a[psin+j].mult(x[j],prec) + dot
|
76
|
+
end
|
77
|
+
x <<= b[ps[i]] - dot
|
78
|
+
end
|
79
|
+
(n-1).downto(0) do |i|
|
80
|
+
dot = zero
|
81
|
+
psin = ps[i]*n
|
82
|
+
for j in (i+1)...n do
|
83
|
+
dot = a[psin+j].mult(x[j],prec) + dot
|
84
|
+
end
|
85
|
+
x[i] = (x[i]-dot).div(a[psin+i],prec)
|
86
|
+
end
|
87
|
+
x
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require 'bigdecimal'
|
3
|
+
|
4
|
+
#
|
5
|
+
#--
|
6
|
+
# Contents:
|
7
|
+
# sqrt(x, prec)
|
8
|
+
# sin (x, prec)
|
9
|
+
# cos (x, prec)
|
10
|
+
# atan(x, prec) Note: |x|<1, x=0.9999 may not converge.
|
11
|
+
# PI (prec)
|
12
|
+
# E (prec) == exp(1.0,prec)
|
13
|
+
#
|
14
|
+
# where:
|
15
|
+
# x ... BigDecimal number to be computed.
|
16
|
+
# |x| must be small enough to get convergence.
|
17
|
+
# prec ... Number of digits to be obtained.
|
18
|
+
#++
|
19
|
+
#
|
20
|
+
# Provides mathematical functions.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
#
|
24
|
+
# require "bigdecimal/math"
|
25
|
+
#
|
26
|
+
# include BigMath
|
27
|
+
#
|
28
|
+
# a = BigDecimal((PI(100)/2).to_s)
|
29
|
+
# puts sin(a,100) # => 0.99999999999999999999......e0
|
30
|
+
#
|
31
|
+
module BigMath
|
32
|
+
module_function
|
33
|
+
|
34
|
+
# call-seq:
|
35
|
+
# sqrt(decimal, numeric) -> BigDecimal
|
36
|
+
#
|
37
|
+
# Computes the square root of +decimal+ to the specified number of digits of
|
38
|
+
# precision, +numeric+.
|
39
|
+
#
|
40
|
+
# BigMath.sqrt(BigDecimal('2'), 16).to_s
|
41
|
+
# #=> "0.1414213562373095048801688724e1"
|
42
|
+
#
|
43
|
+
def sqrt(x, prec)
|
44
|
+
x.sqrt(prec)
|
45
|
+
end
|
46
|
+
|
47
|
+
# call-seq:
|
48
|
+
# sin(decimal, numeric) -> BigDecimal
|
49
|
+
#
|
50
|
+
# Computes the sine of +decimal+ to the specified number of digits of
|
51
|
+
# precision, +numeric+.
|
52
|
+
#
|
53
|
+
# If +decimal+ is Infinity or NaN, returns NaN.
|
54
|
+
#
|
55
|
+
# BigMath.sin(BigMath.PI(5)/4, 5).to_s
|
56
|
+
# #=> "0.70710678118654752440082036563292800375e0"
|
57
|
+
#
|
58
|
+
def sin(x, prec)
|
59
|
+
raise ArgumentError, "Zero or negative precision for sin" if prec <= 0
|
60
|
+
return BigDecimal("NaN") if x.infinite? || x.nan?
|
61
|
+
n = prec + BigDecimal.double_fig
|
62
|
+
one = BigDecimal("1")
|
63
|
+
two = BigDecimal("2")
|
64
|
+
x = -x if neg = x < 0
|
65
|
+
if x > (twopi = two * BigMath.PI(prec))
|
66
|
+
if x > 30
|
67
|
+
x %= twopi
|
68
|
+
else
|
69
|
+
x -= twopi while x > twopi
|
70
|
+
end
|
71
|
+
end
|
72
|
+
x1 = x
|
73
|
+
x2 = x.mult(x,n)
|
74
|
+
sign = 1
|
75
|
+
y = x
|
76
|
+
d = y
|
77
|
+
i = one
|
78
|
+
z = one
|
79
|
+
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
80
|
+
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
81
|
+
sign = -sign
|
82
|
+
x1 = x2.mult(x1,n)
|
83
|
+
i += two
|
84
|
+
z *= (i-one) * i
|
85
|
+
d = sign * x1.div(z,m)
|
86
|
+
y += d
|
87
|
+
end
|
88
|
+
neg ? -y : y
|
89
|
+
end
|
90
|
+
|
91
|
+
# call-seq:
|
92
|
+
# cos(decimal, numeric) -> BigDecimal
|
93
|
+
#
|
94
|
+
# Computes the cosine of +decimal+ to the specified number of digits of
|
95
|
+
# precision, +numeric+.
|
96
|
+
#
|
97
|
+
# If +decimal+ is Infinity or NaN, returns NaN.
|
98
|
+
#
|
99
|
+
# BigMath.cos(BigMath.PI(4), 16).to_s
|
100
|
+
# #=> "-0.999999999999999999999999999999856613163740061349e0"
|
101
|
+
#
|
102
|
+
def cos(x, prec)
|
103
|
+
raise ArgumentError, "Zero or negative precision for cos" if prec <= 0
|
104
|
+
return BigDecimal("NaN") if x.infinite? || x.nan?
|
105
|
+
n = prec + BigDecimal.double_fig
|
106
|
+
one = BigDecimal("1")
|
107
|
+
two = BigDecimal("2")
|
108
|
+
x = -x if x < 0
|
109
|
+
if x > (twopi = two * BigMath.PI(prec))
|
110
|
+
if x > 30
|
111
|
+
x %= twopi
|
112
|
+
else
|
113
|
+
x -= twopi while x > twopi
|
114
|
+
end
|
115
|
+
end
|
116
|
+
x1 = one
|
117
|
+
x2 = x.mult(x,n)
|
118
|
+
sign = 1
|
119
|
+
y = one
|
120
|
+
d = y
|
121
|
+
i = BigDecimal("0")
|
122
|
+
z = one
|
123
|
+
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
124
|
+
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
125
|
+
sign = -sign
|
126
|
+
x1 = x2.mult(x1,n)
|
127
|
+
i += two
|
128
|
+
z *= (i-one) * i
|
129
|
+
d = sign * x1.div(z,m)
|
130
|
+
y += d
|
131
|
+
end
|
132
|
+
y
|
133
|
+
end
|
134
|
+
|
135
|
+
# call-seq:
|
136
|
+
# atan(decimal, numeric) -> BigDecimal
|
137
|
+
#
|
138
|
+
# Computes the arctangent of +decimal+ to the specified number of digits of
|
139
|
+
# precision, +numeric+.
|
140
|
+
#
|
141
|
+
# If +decimal+ is NaN, returns NaN.
|
142
|
+
#
|
143
|
+
# BigMath.atan(BigDecimal('-1'), 16).to_s
|
144
|
+
# #=> "-0.785398163397448309615660845819878471907514682065e0"
|
145
|
+
#
|
146
|
+
def atan(x, prec)
|
147
|
+
raise ArgumentError, "Zero or negative precision for atan" if prec <= 0
|
148
|
+
return BigDecimal("NaN") if x.nan?
|
149
|
+
pi = PI(prec)
|
150
|
+
x = -x if neg = x < 0
|
151
|
+
return pi.div(neg ? -2 : 2, prec) if x.infinite?
|
152
|
+
return pi / (neg ? -4 : 4) if x.round(prec) == 1
|
153
|
+
x = BigDecimal("1").div(x, prec) if inv = x > 1
|
154
|
+
x = (-1 + sqrt(1 + x**2, prec))/x if dbl = x > 0.5
|
155
|
+
n = prec + BigDecimal.double_fig
|
156
|
+
y = x
|
157
|
+
d = y
|
158
|
+
t = x
|
159
|
+
r = BigDecimal("3")
|
160
|
+
x2 = x.mult(x,n)
|
161
|
+
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
162
|
+
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
163
|
+
t = -t.mult(x2,n)
|
164
|
+
d = t.div(r,m)
|
165
|
+
y += d
|
166
|
+
r += 2
|
167
|
+
end
|
168
|
+
y *= 2 if dbl
|
169
|
+
y = pi / 2 - y if inv
|
170
|
+
y = -y if neg
|
171
|
+
y
|
172
|
+
end
|
173
|
+
|
174
|
+
# call-seq:
|
175
|
+
# PI(numeric) -> BigDecimal
|
176
|
+
#
|
177
|
+
# Computes the value of pi to the specified number of digits of precision,
|
178
|
+
# +numeric+.
|
179
|
+
#
|
180
|
+
# BigMath.PI(10).to_s
|
181
|
+
# #=> "0.3141592653589793238462643388813853786957412e1"
|
182
|
+
#
|
183
|
+
def PI(prec)
|
184
|
+
raise ArgumentError, "Zero or negative precision for PI" if prec <= 0
|
185
|
+
n = prec + BigDecimal.double_fig
|
186
|
+
zero = BigDecimal("0")
|
187
|
+
one = BigDecimal("1")
|
188
|
+
two = BigDecimal("2")
|
189
|
+
|
190
|
+
m25 = BigDecimal("-0.04")
|
191
|
+
m57121 = BigDecimal("-57121")
|
192
|
+
|
193
|
+
pi = zero
|
194
|
+
|
195
|
+
d = one
|
196
|
+
k = one
|
197
|
+
t = BigDecimal("-80")
|
198
|
+
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
|
199
|
+
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
200
|
+
t = t*m25
|
201
|
+
d = t.div(k,m)
|
202
|
+
k = k+two
|
203
|
+
pi = pi + d
|
204
|
+
end
|
205
|
+
|
206
|
+
d = one
|
207
|
+
k = one
|
208
|
+
t = BigDecimal("956")
|
209
|
+
while d.nonzero? && ((m = n - (pi.exponent - d.exponent).abs) > 0)
|
210
|
+
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
211
|
+
t = t.div(m57121,n)
|
212
|
+
d = t.div(k,m)
|
213
|
+
pi = pi + d
|
214
|
+
k = k+two
|
215
|
+
end
|
216
|
+
pi
|
217
|
+
end
|
218
|
+
|
219
|
+
# call-seq:
|
220
|
+
# E(numeric) -> BigDecimal
|
221
|
+
#
|
222
|
+
# Computes e (the base of natural logarithms) to the specified number of
|
223
|
+
# digits of precision, +numeric+.
|
224
|
+
#
|
225
|
+
# BigMath.E(10).to_s
|
226
|
+
# #=> "0.271828182845904523536028752390026306410273e1"
|
227
|
+
#
|
228
|
+
def E(prec)
|
229
|
+
raise ArgumentError, "Zero or negative precision for E" if prec <= 0
|
230
|
+
BigMath.exp(1, prec)
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require "bigdecimal/ludcmp"
|
3
|
+
require "bigdecimal/jacobian"
|
4
|
+
|
5
|
+
#
|
6
|
+
# newton.rb
|
7
|
+
#
|
8
|
+
# Solves the nonlinear algebraic equation system f = 0 by Newton's method.
|
9
|
+
# This program is not dependent on BigDecimal.
|
10
|
+
#
|
11
|
+
# To call:
|
12
|
+
# n = nlsolve(f,x)
|
13
|
+
# where n is the number of iterations required,
|
14
|
+
# x is the initial value vector
|
15
|
+
# f is an Object which is used to compute the values of the equations to be solved.
|
16
|
+
# It must provide the following methods:
|
17
|
+
#
|
18
|
+
# f.values(x):: returns the values of all functions at x
|
19
|
+
#
|
20
|
+
# f.zero:: returns 0.0
|
21
|
+
# f.one:: returns 1.0
|
22
|
+
# f.two:: returns 2.0
|
23
|
+
# f.ten:: returns 10.0
|
24
|
+
#
|
25
|
+
# f.eps:: returns the convergence criterion (epsilon value) used to determine whether two values are considered equal. If |a-b| < epsilon, the two values are considered equal.
|
26
|
+
#
|
27
|
+
# On exit, x is the solution vector.
|
28
|
+
#
|
29
|
+
module Newton
|
30
|
+
include LUSolve
|
31
|
+
include Jacobian
|
32
|
+
module_function
|
33
|
+
|
34
|
+
def norm(fv,zero=0.0) # :nodoc:
|
35
|
+
s = zero
|
36
|
+
n = fv.size
|
37
|
+
for i in 0...n do
|
38
|
+
s += fv[i]*fv[i]
|
39
|
+
end
|
40
|
+
s
|
41
|
+
end
|
42
|
+
|
43
|
+
# See also Newton
|
44
|
+
def nlsolve(f,x)
|
45
|
+
nRetry = 0
|
46
|
+
n = x.size
|
47
|
+
|
48
|
+
f0 = f.values(x)
|
49
|
+
zero = f.zero
|
50
|
+
one = f.one
|
51
|
+
two = f.two
|
52
|
+
p5 = one/two
|
53
|
+
d = norm(f0,zero)
|
54
|
+
minfact = f.ten*f.ten*f.ten
|
55
|
+
minfact = one/minfact
|
56
|
+
e = f.eps
|
57
|
+
while d >= e do
|
58
|
+
nRetry += 1
|
59
|
+
# Not yet converged. => Compute Jacobian matrix
|
60
|
+
dfdx = jacobian(f,f0,x)
|
61
|
+
# Solve dfdx*dx = -f0 to estimate dx
|
62
|
+
dx = lusolve(dfdx,f0,ludecomp(dfdx,n,zero,one),zero)
|
63
|
+
fact = two
|
64
|
+
xs = x.dup
|
65
|
+
begin
|
66
|
+
fact *= p5
|
67
|
+
if fact < minfact then
|
68
|
+
raise "Failed to reduce function values."
|
69
|
+
end
|
70
|
+
for i in 0...n do
|
71
|
+
x[i] = xs[i] - dx[i]*fact
|
72
|
+
end
|
73
|
+
f0 = f.values(x)
|
74
|
+
dn = norm(f0,zero)
|
75
|
+
end while(dn>=d)
|
76
|
+
d = dn
|
77
|
+
end
|
78
|
+
nRetry
|
79
|
+
end
|
80
|
+
end
|