appmath 0.0.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.
- data/bin/kepler_2d_app.rb +130 -0
- data/bin/linalg_app.rb +193 -0
- data/bin/rnum_app.rb +199 -0
- data/gpl-3.0.txt +674 -0
- data/lib/appmath_basics.rb +118 -0
- data/lib/cnum.rb +615 -0
- data/lib/float_ext.rb +223 -0
- data/lib/graph.rb +415 -0
- data/lib/interval.rb +282 -0
- data/lib/kepler_2d.rb +162 -0
- data/lib/linalg.rb +1309 -0
- data/lib/random.rb +88 -0
- data/lib/rnum.rb +1648 -0
- data/readme.txt +126 -0
- metadata +72 -0
data/lib/random.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
ruby
|
3
|
+
|
4
|
+
Ulrich Mutze, www.ulrichmutze.de
|
5
|
+
|
6
|
+
Started 2008-12-09
|
7
|
+
|
8
|
+
Defines class AppMath::Ran.
|
9
|
+
|
10
|
+
Requires file interval.
|
11
|
+
|
12
|
+
Copyright (C) 2008 Ulrich Mutze
|
13
|
+
|
14
|
+
This program is free software: you can redistribute it and/or modify
|
15
|
+
it under the terms of the GNU General Public License as published by
|
16
|
+
the Free Software Foundation, either version 3 of the License, or
|
17
|
+
(at your option) any later version.
|
18
|
+
|
19
|
+
This program is distributed in the hope that it will be useful,
|
20
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
21
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
22
|
+
GNU General Public License for more details.
|
23
|
+
|
24
|
+
You should have received a copy of the GNU General Public License
|
25
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
26
|
+
=end
|
27
|
+
|
28
|
+
require File.join(File.dirname(__FILE__), 'interval')
|
29
|
+
#require 'interval'
|
30
|
+
|
31
|
+
module AppMath
|
32
|
+
|
33
|
+
=begin rdoc
|
34
|
+
Class of random generators.
|
35
|
+
The real numbers delivered by different random generators are intended
|
36
|
+
to be statistically independent. Each random generator has its own
|
37
|
+
interval within which the output values are uniformly distributed.
|
38
|
+
=end
|
39
|
+
|
40
|
+
class Ran
|
41
|
+
|
42
|
+
attr_reader :iv
|
43
|
+
@@seed = 137
|
44
|
+
@@swing = 133216711
|
45
|
+
|
46
|
+
# The arguments have to determine an interval in which the random values
|
47
|
+
# lie. This is named @iv. If there is no argument we have @iv = [0,1].
|
48
|
+
# If there is one argument, it has to be an instance of Iv and then
|
49
|
+
# becomes @iv. If there are two arguments they need to be convertible to
|
50
|
+
# R and then become the boundaries of @iv.
|
51
|
+
def initialize(*arg)
|
52
|
+
n = arg.size
|
53
|
+
case n
|
54
|
+
when 0
|
55
|
+
@iv = Iv.new(0,1)
|
56
|
+
when 1
|
57
|
+
a0 = arg[0]
|
58
|
+
fail "argument must be an Iv" unless a0.class == Iv
|
59
|
+
@iv = a0
|
60
|
+
when 2
|
61
|
+
a0 = arg[0]; a1 = arg[1]
|
62
|
+
@iv = Iv.new(a0,a1)
|
63
|
+
else
|
64
|
+
fail "0,1,or 2 arguments needed, not #{n}"
|
65
|
+
end
|
66
|
+
x = R.ran(@@seed)
|
67
|
+
i = (@@swing * x).to_i
|
68
|
+
@local_seed = i
|
69
|
+
@@seed += 1
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns a point (value) from interval @iv by
|
73
|
+
# 'random selection'. The underlying mechanism is the
|
74
|
+
# sine-floor generator defined in method R.ran.
|
75
|
+
def ran
|
76
|
+
p = R.ran(@local_seed)
|
77
|
+
@local_seed += 1
|
78
|
+
@iv.put(p)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the local seed for control purposes. Should not be needed.
|
82
|
+
def local_seed
|
83
|
+
@local_seed
|
84
|
+
end
|
85
|
+
|
86
|
+
end # class Ran
|
87
|
+
|
88
|
+
end # AppMath
|
data/lib/rnum.rb
ADDED
@@ -0,0 +1,1648 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
ruby
|
3
|
+
|
4
|
+
Ulrich Mutze, www.ulrichmutze.de
|
5
|
+
|
6
|
+
Started 2008-11-01 as experimentation with class BigDecimal.
|
7
|
+
|
8
|
+
Defines class AppMath::R.
|
9
|
+
|
10
|
+
Requires files bigdecimal and bigdecimal/math.
|
11
|
+
|
12
|
+
Copyright (C) 2008 Ulrich Mutze
|
13
|
+
|
14
|
+
This program is free software: you can redistribute it and/or modify
|
15
|
+
it under the terms of the GNU General Public License as published by
|
16
|
+
the Free Software Foundation, either version 3 of the License, or
|
17
|
+
(at your option) any later version.
|
18
|
+
|
19
|
+
This program is distributed in the hope that it will be useful,
|
20
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
21
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
22
|
+
GNU General Public License for more details.
|
23
|
+
|
24
|
+
You should have received a copy of the GNU General Public License
|
25
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
26
|
+
=end
|
27
|
+
|
28
|
+
# Applied mathematics. Emphasis is on precision-independent coding.
|
29
|
+
module AppMath
|
30
|
+
|
31
|
+
require 'bigdecimal'
|
32
|
+
require 'bigdecimal/math'
|
33
|
+
include BigMath
|
34
|
+
|
35
|
+
=begin rdoc
|
36
|
+
Class of real numbers which implements arbitrary precision arithmetic
|
37
|
+
together with the standard elementary transcendental functions.
|
38
|
+
|
39
|
+
Implementation is based on classes BigDecimal and module BigMath
|
40
|
+
created by Shigeo Kobayashi.
|
41
|
+
|
42
|
+
The term 'precision' is here used in one of its technical meanings:
|
43
|
+
here it is the number of decimal digits used for representing the
|
44
|
+
'significand' of a number.
|
45
|
+
Recall that each real number can be represented uniquely as
|
46
|
+
sign * significand * 10^exponent
|
47
|
+
where
|
48
|
+
sign = +/-
|
49
|
+
significand = 0.d_1 d_2 d_3 ...., where the d_1, d_2, are
|
50
|
+
decimal digits in 0,1,2,3,4,5,6,7,8,9, but d_1 != 0
|
51
|
+
exponent is an integer number
|
52
|
+
For any natural number n, a real number 'of precision n' is one
|
53
|
+
for which the digits d_i vanish for all i > n.
|
54
|
+
|
55
|
+
Notice that specifying the precision defines an infinite subset of
|
56
|
+
the rational numbers. The infinity is due to the infinity of choices
|
57
|
+
for the exponent. In the class BigDecimal, there is the restriction
|
58
|
+
that the exponent be a Fixnum (and not a Bignum) so that there is
|
59
|
+
a well-defined large but finite number of possible exponents in
|
60
|
+
BigDecimal and so there is a well-defined finite set of BigDecimals
|
61
|
+
for any specified value of the precision.
|
62
|
+
|
63
|
+
Unlike class BigDecimal, the present class R (recall the mathematical
|
64
|
+
standard symbols N, Z, Q, R, C, H for natural, integer, rational,
|
65
|
+
real, complex, quaternionic numbers) treats precision not as a
|
66
|
+
parameter of the arithmetic operations but as a class variable:
|
67
|
+
The computational representation of the real number world as a whole
|
68
|
+
depends on the precision parameter. Class R makes it easy to
|
69
|
+
write programs in a 'precision independent style':
|
70
|
+
With a single statement, e. g.
|
71
|
+
R.prec = 100
|
72
|
+
one selects the precision of all numbers (here as 100 decimal places),
|
73
|
+
and all arithmetic operations and transcendental functions take the
|
74
|
+
required precision automaticaly int account. There is an important
|
75
|
+
point here: Obviously the exact product of two numbers of precsion p
|
76
|
+
is normally a number of precision 2p. Class BigDecimal allows to
|
77
|
+
obtain the exact value of a product if the precision parameter of
|
78
|
+
multiplication remains unspecified (or is specified as 0).
|
79
|
+
This exact arithmetic, however, gives longer and longer numbers and
|
80
|
+
slower and slower execution in virtually all algorithms.
|
81
|
+
Thus cutting all arithmetic results back to precission p is essential
|
82
|
+
for algorithms of real analysis to remain executable in a practical sense.
|
83
|
+
|
84
|
+
Since arbitrary precision arithmetic, not only for very large precision,
|
85
|
+
is considerably slower than Float arithmetic (I found a factor 3.6
|
86
|
+
for a singular value decomposition of a 20 times 20 matrix between
|
87
|
+
precision 17 computation time and Float computation time. Precision 100
|
88
|
+
led to a computation time of 5.39 s, which was 2.7 times longer than
|
89
|
+
for precision 17.) it is desirable to allow switching to Float arithmetic
|
90
|
+
with the same syntax:
|
91
|
+
R.prec = 0
|
92
|
+
and
|
93
|
+
R.prec = "float"
|
94
|
+
both have this effect. In order for this to work, one has to introduce
|
95
|
+
real numbers into the program by a simple specific syntax relying on R:
|
96
|
+
Instead of
|
97
|
+
x = 1.456e-6; y = 7.7; z = 1000.0; n = 128
|
98
|
+
a R-based program may use the conventional mehod 'new' and write
|
99
|
+
x = R.new("1.456E-6"); y = R.new(7.7); z = R.new(1000); n = 128
|
100
|
+
Instead, we also may use the class method c ('c' for 'converter') and
|
101
|
+
write (with or without argument brackets)
|
102
|
+
x = R.c 1.456e-6; y = R.c(7.7); z = R.c 1000; n = 128
|
103
|
+
Written in this form, the variables x,y,z refer to R-objects for precision
|
104
|
+
set as a positive integer, and to Float-objects as a consequence of the
|
105
|
+
statement
|
106
|
+
R.prec = "float"
|
107
|
+
Assume that the program continues as
|
108
|
+
u = (x.sin + y.exp) * z
|
109
|
+
This works for R-objects since R implements as methods all the functions
|
110
|
+
which module Math povides as module functions. When, however, we have the
|
111
|
+
statement R.prec = "float" in action, the variables x and y refer to
|
112
|
+
Float objects. Then, the terms x.sin and y.exp are not defined, unless we
|
113
|
+
prevented the problem via
|
114
|
+
require 'float_ext'
|
115
|
+
which adds to the interface of Float all the methods of R so that any program
|
116
|
+
hat makes sense for R, also makes sense for Float. (This is a harmless
|
117
|
+
change of Float that breaks no code which relied on the unmodified
|
118
|
+
class Float. If Float has been modified already for some reason, one has to
|
119
|
+
make sure that this extension does not conflict with the previous one.)
|
120
|
+
Instead of setting precision for the whole program, we may vary it for
|
121
|
+
direct comparison by code like this:
|
122
|
+
|
123
|
+
for i in 0..8
|
124
|
+
R.prec = 20*i
|
125
|
+
# some computation, e. g.
|
126
|
+
t1 = Time.now
|
127
|
+
x = [R.c2, R.i2, R.i(0.33333),R.ran(137), R.tob(i), R.pi]
|
128
|
+
x.each{ |x|
|
129
|
+
y = x.sin**2 + x.cos**2 - 1
|
130
|
+
puts "x = #{x.round(6)}, y = #{y}"
|
131
|
+
}
|
132
|
+
t2 = Time.now
|
133
|
+
puts "computation time was #{t2 - t1} for precision #{R.prec}"
|
134
|
+
end
|
135
|
+
|
136
|
+
Here all precision independent creation modes for real numbers are
|
137
|
+
exemplified:
|
138
|
+
R.c2 equivalent to the yet known R.c(2), hence 2
|
139
|
+
R.i2 inverse of 2, i.e. 0.5
|
140
|
+
R.i(0.33333) inverse of 0.33333 i.e. approximately 3
|
141
|
+
R.ran(137) sine-floor random generator invoked for argument 137
|
142
|
+
R.tob(i) test object generator invoked for agument i
|
143
|
+
R.pi number pi = 3.14159... (also R.e = 2.71828...)
|
144
|
+
|
145
|
+
Since the method 'coerce', which determines the meaning of binary
|
146
|
+
operator expressions, is defined appropriately in R, one may for a
|
147
|
+
R-object r write 1/r instead of R.c1/r and r + 1 instead of
|
148
|
+
r + R.c1. This allows us to avoid the appearance of R in all formulas
|
149
|
+
except those where real variables are initialized, as in the part
|
150
|
+
x = [R.c2, R.i2, R.i(0.33333),R.ran(137), R.tob(3), R.pi]
|
151
|
+
of our example computation.
|
152
|
+
|
153
|
+
Class R is truly consitent with Ruby's numerical concepts: R is derived
|
154
|
+
from Ruby class Numeric so that R, Float, Fixnum, and Bignum have a common
|
155
|
+
superclass. Notice that class BigDecimal cannot have this property since
|
156
|
+
most of its functions need the precision parameter as input.
|
157
|
+
Further, R implements as member funcions all functions which are
|
158
|
+
defined in Ruby module Math and thus form the core of Ruby's mathematics.
|
159
|
+
It would therefore be natural to integrate class R into the BigMath
|
160
|
+
module. It would also be natural to include float_ext.rb since this would
|
161
|
+
enable the 'precision-independent coding style' scetched above within
|
162
|
+
all number-oriented Ruby projects. One would then have a framework in
|
163
|
+
which valuable algorithms such as singular value decomposition of matrices
|
164
|
+
could be coded with permanent validity. I achieved to transform
|
165
|
+
real matrix SVD code from 'Numerical Recipes in C' by Press et al.
|
166
|
+
to precision-independent Ruby code and did not succeed so far with the
|
167
|
+
same task for complex matrices.
|
168
|
+
Inspecting existing sources for "Algorithm 358" shows how far we are away
|
169
|
+
fom presenting algorithms of typical complexity in a form that is ready
|
170
|
+
for usage at arbitrary precision. In order for this task to make sense,
|
171
|
+
one has to have complex numbers, vectors and matrices in
|
172
|
+
precision-independent Ruby style. As far as I can see it, the many
|
173
|
+
mathematical tool boxes provided by the Ruby community don't contain the
|
174
|
+
tools which are needed here. The Ruby language makes it pure fun, however, to
|
175
|
+
build these tools according to ones needs, and I did it with the results
|
176
|
+
available from www.ulrichmutze.de. I think that it is extremely important
|
177
|
+
to have an agreed framework for coding mathematical and physical algorithms.
|
178
|
+
Only then one can hope that people will start to add their work and use
|
179
|
+
the work of others. So far, I have collected all the algoritms that I ever
|
180
|
+
had the opportunity to use in my CPM class system, which is written in
|
181
|
+
C++, using a programming style that I call C+-.
|
182
|
+
This is presented on my website.
|
183
|
+
|
184
|
+
Having now a tiny part of the CPM system carried over (not at all
|
185
|
+
verbatim) to Ruby, I expect that a Ruby version of the whole system would
|
186
|
+
be much smaller and more compact. I'm quite sure that I'll lack enthusiasm
|
187
|
+
(and even power) to create such an extended algorithm collection in Ruby
|
188
|
+
myself.
|
189
|
+
Would it not be great, if any algorithm (e.g. eigen-vectors/values of
|
190
|
+
matrices, FFT, min and root finding) that finds its way into some Ruby
|
191
|
+
library, would be written in a way that allows it to be executed in
|
192
|
+
arbitrary precision, even if Float precision is the preferred option?
|
193
|
+
The present class R and also the other classes to be collected in the
|
194
|
+
present module AppMath, should add to the experience which is needed to
|
195
|
+
come up with a working method to achieve this goal.
|
196
|
+
|
197
|
+
We know from the beginning that in BigDecimal and hence in R the
|
198
|
+
exponents of numbers are restricted to the size which can be represented
|
199
|
+
by a Fixnum. Although computations in physics and engineering are far
|
200
|
+
from coming close to this limit, schematically applied mathematical
|
201
|
+
functions easily transcend it:
|
202
|
+
|
203
|
+
R.prec = 60
|
204
|
+
a = R.c 2.2
|
205
|
+
for i in 0...9
|
206
|
+
a = a.exp
|
207
|
+
puts "a = " + a.to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
yields
|
211
|
+
|
212
|
+
a = 0.902501349943412092647177716688866402972021659669817926079804E1
|
213
|
+
a = 0.830832663077249493655084378868900432568369546441921929731276E4
|
214
|
+
a = 0.182141787499134800567191386180195980368655533517234407096707E3609
|
215
|
+
a = 0.0
|
216
|
+
a = 0.1E1
|
217
|
+
a = 0.271828182845904523536028747135266249775724709369995957496696E1
|
218
|
+
a = 0.151542622414792641897604302726299119055285485368561397691404E2
|
219
|
+
a = 0.381427910476022059220921959409820357102394053622666607552533E7
|
220
|
+
a = 0.233150439900719546228968991101213766633201742896351361434871E1656521
|
221
|
+
|
222
|
+
where only the first three results are correct and the following ones are
|
223
|
+
determined by the overfow behavior of Fixnum.
|
224
|
+
|
225
|
+
|
226
|
+
Motivation and rational
|
227
|
+
-----------------------
|
228
|
+
|
229
|
+
Experimentation with class R provided many experiences which sharpened my
|
230
|
+
long-standing diffuse ideas concerning a coding framework for mathematics.
|
231
|
+
|
232
|
+
In mathematics we have 'two worlds': the discrete one and the 'continuous'
|
233
|
+
one. For the first world, Ruby is fit without modifications. Its automatic
|
234
|
+
switch from Fixnum representation of integer numbers to a Bignum
|
235
|
+
representation eliminates all limitations to the coding of discrete
|
236
|
+
mathematics problems for which a coding approach is reasonable from a
|
237
|
+
logical point of view.
|
238
|
+
|
239
|
+
The 'continuous world' is the one for which the real numbers lay the ground.
|
240
|
+
The prime example of this world is 'real analysis' for which the
|
241
|
+
concept of convergence is considered central. When a computationally
|
242
|
+
oriented scientist describes to a pure mathematician his experience that
|
243
|
+
all mathematically and physically relevant structures seem to have natural
|
244
|
+
codable counterparts, this pure mathematician will probably admit that
|
245
|
+
this holds for the trivial part of the story, but he will probably
|
246
|
+
insist that all deeper questions, those concerning convergence,
|
247
|
+
closedness, and completeness are a priori outside the scope of numerical
|
248
|
+
methods.
|
249
|
+
|
250
|
+
According to my understanding, this mathematician's point of view is
|
251
|
+
misleading. It is, however, suggested by what mathematicians actually do.
|
252
|
+
For them, it is natural, when having to work with a number the square of
|
253
|
+
which is known to be 2, to 'construct' this number as a limit of
|
254
|
+
objects (e.g. finite decimal fractions) with the intention to use this
|
255
|
+
'exact solution of the equation x^2 = 2' in further constructions and
|
256
|
+
deductions within the framework of real analysis.
|
257
|
+
|
258
|
+
For a computationally oriented scientist an alterative view is more
|
259
|
+
natural and more promising: Do everything (i.e solving x^2 = 2, and the
|
260
|
+
further constructions and deductions mentioned above)
|
261
|
+
with finite precision and consider this 'whole thing' (and not only x)
|
262
|
+
as a function of this precision.
|
263
|
+
When we consider the behavior for growing precision of the
|
264
|
+
'whole thing' we have a single limit (if we need to consider a
|
265
|
+
limit at all), and the question never arises whether two limits
|
266
|
+
are allowed to be interchanged. Such questions are typically not
|
267
|
+
easy and a major part of the technical scills of mathematicians
|
268
|
+
is devoted to them. I'm quite sure that I spent more than a year of my
|
269
|
+
live struggling with such questions.
|
270
|
+
|
271
|
+
To have a realistic example, let us consider that the 'whole thing',
|
272
|
+
mentioned above, is the task to produce all tables, figures, diagrams,
|
273
|
+
and animations that will go into a presentation or publication. Then it is
|
274
|
+
natural to consider a large structured Ruby program as the means to
|
275
|
+
perform this task. (My experience is restricted to C++ programs instead
|
276
|
+
of Ruby programs for this situation.)
|
277
|
+
Let us assume that all data that normally would be
|
278
|
+
represented as Float objects, now are represented as R objects.
|
279
|
+
As discussed already, this means that e.g. instead of
|
280
|
+
x = 2.0
|
281
|
+
we hat to write
|
282
|
+
x = R.c(2.0)
|
283
|
+
or
|
284
|
+
x = R.c2
|
285
|
+
or we had to add to the statement
|
286
|
+
x = 2.0
|
287
|
+
the conversion statement
|
288
|
+
x = R.c(x)
|
289
|
+
No modification or conversion would be needed for the integer numbers!
|
290
|
+
Now consider having an initial statement
|
291
|
+
|
292
|
+
R.prec = "float"
|
293
|
+
|
294
|
+
in the main program flow.
|
295
|
+
By executing the program we get curves in diagrams, moving particles
|
296
|
+
in animations, etc. If some of these curves are more jagged than expected,
|
297
|
+
or some table values turn out to be NaN or Infinite we may try
|
298
|
+
R.prec = 40
|
299
|
+
and
|
300
|
+
R.prec = 80
|
301
|
+
...
|
302
|
+
If the results stabilize somewhere, practitioners are sure that
|
303
|
+
'the result is now independent of numerical errors'.
|
304
|
+
Of course, the mathematician's objection that behavior for a few finite
|
305
|
+
values says nothing about the limit, also applies here.
|
306
|
+
But we are in a very comfortable position to cope with this objection:
|
307
|
+
Since we deal with the solution of a task, we are allowed to assume that
|
308
|
+
we know the experience-based conventions and 'best practices' concerning
|
309
|
+
solutions of tasks in the problem field under consideration. So, the
|
310
|
+
judgement whether the behavior of the solution as a function of precision
|
311
|
+
supports a specific conclusion or not has a firm basis when
|
312
|
+
the context is taken into account. It is an illusionary hope that
|
313
|
+
some abstract framework such as 'Mathematics' could replace the guidance
|
314
|
+
provided by problem-field related knowledge and experience.
|
315
|
+
|
316
|
+
Let me finally describe an experience which suggested to me the
|
317
|
+
concept of precision as a parameter of whole task (project) instead
|
318
|
+
of a parameter of individual arithmetic operations:
|
319
|
+
|
320
|
+
In an industrial project I had to assess the influence of lens aberrations
|
321
|
+
to the efficiency of coupling laser light into the core of an optical
|
322
|
+
fiber.
|
323
|
+
Although the situation is clearly a wave-optical one, the idea was to test
|
324
|
+
whether a ray-tracing simulation would reproduce the few available
|
325
|
+
measured data.
|
326
|
+
In case of success one would rely on ray-tracing for unexpensive
|
327
|
+
optimization and tolerance analysis.
|
328
|
+
At those times, in my company all computation was done
|
329
|
+
in FORTRAN and floating point number representation was by 4 bytes
|
330
|
+
(just as float in C).
|
331
|
+
The simulation reproduced the measured curve not really but it
|
332
|
+
wiggled arround it in a remarkably symmetrical manner. Just at this time
|
333
|
+
our FORTRAN compiler was upgraded to provide 8-byte numbers (corresonding
|
334
|
+
to C's double). What I had suspected turned out to be true:
|
335
|
+
the ray-trace simulation now reproduced the measurements with magical
|
336
|
+
precision.
|
337
|
+
Congratulations to the optical lab for having got such highly consistent
|
338
|
+
data!
|
339
|
+
Soon I turned to programming in C and enjoyed many advantages over
|
340
|
+
FORTRAN, but one paradise was lost: There was no longer a meaningful
|
341
|
+
comparison between 4 byte and 8 byte precision since the available C-compiler
|
342
|
+
worked with 8 bytes internally in both cases. When later we got the type
|
343
|
+
long double in additon, this was an disappointment since it was identical to
|
344
|
+
double for the MS-compiler and only 10 bytes (?) for GNU. So my desire was to
|
345
|
+
have a C++ compiler which implemented, and consistenly used
|
346
|
+
float: 4 bytes
|
347
|
+
double: 8 bytes
|
348
|
+
long double: 16 bytes
|
349
|
+
I then would write all my programs with a floating point type named R
|
350
|
+
which gets its real meaning in a flexible manner by a statement like
|
351
|
+
typedef long double R;
|
352
|
+
I was rather sure for a long time that I would never meet a practical
|
353
|
+
situation in which a simulation would show objectionable numerical
|
354
|
+
errors when done with 16 byte numbers. I had to learn, however, that this
|
355
|
+
was a silly idea: Let us consider a system of polyspherical elastic
|
356
|
+
particles (the shape of each particle is a union of overlapping spheres)
|
357
|
+
which are placed in a mirror-symmetrical container and let initial
|
358
|
+
positions (placements) and velocities be arranged symmetrically
|
359
|
+
(with respect to the same mirror).
|
360
|
+
Then the exact motion can be seen to preserve this symmetry.
|
361
|
+
However, each simulation will loose this symmetry after a few particle to
|
362
|
+
particle collisions. (The particles start to rotate after collisions,
|
363
|
+
which influences further collisions much more than for mono-spherical
|
364
|
+
particles.) Increasing the number of bytes per number will only allow a
|
365
|
+
few more symmetrical collisions, and the computation time needed to see
|
366
|
+
these will increase.
|
367
|
+
Of coarse, this deviation from symmetry is not objectionable from a
|
368
|
+
'real world' point of view.
|
369
|
+
It teaches the positive fact that tolerances for making the particles,
|
370
|
+
placing and boosting them, are unrealisticly tight for realizing a
|
371
|
+
symmetrical motion of the system. This shows that there are
|
372
|
+
computational tasks in which not all computed system properties
|
373
|
+
will stabilize with increasing computational precision. With class R
|
374
|
+
such computational phenomena are within the scope of Ruby programming!
|
375
|
+
|
376
|
+
Preferred command line for documentation:
|
377
|
+
rdoc -a -N -S --op doc_r r.rb
|
378
|
+
=end
|
379
|
+
|
380
|
+
class R < Numeric
|
381
|
+
|
382
|
+
attr_reader :g
|
383
|
+
|
384
|
+
# number of stored decimals
|
385
|
+
@@prec = 40
|
386
|
+
# value of number pi = 3.14159...
|
387
|
+
@@pi = BigDecimal("0.3141592653589793238462643383279502884197E1")
|
388
|
+
# value of Euler's number e = 2.718281828...
|
389
|
+
@@e = BigDecimal("0.2718281828459045235360287471352662497757E1")
|
390
|
+
# value of log10(e)
|
391
|
+
@@lge = BigDecimal("0.4342944819032518276511289189166050822944E0")
|
392
|
+
# value of log(10)
|
393
|
+
@@ln10 = BigDecimal("0.2302585092994045684017991454684364207601E1")
|
394
|
+
|
395
|
+
# Setting precision, i. e. the number of digits.
|
396
|
+
#
|
397
|
+
# If the anInteger.to_i is an integer > 0, this number will
|
398
|
+
# be set as the class variable @@prec.
|
399
|
+
# All other input will set @@prec to 0.
|
400
|
+
# Usage:
|
401
|
+
# R.prec = 100
|
402
|
+
# or
|
403
|
+
# R.prec = "float"
|
404
|
+
# A class state @@prec == 0 lets all generating methods create
|
405
|
+
# Float-objects instead of R-objects. Calling methods of
|
406
|
+
# R-objects is undefined in this class state. This is behavior is
|
407
|
+
# reasonable since all real numbers will be created as Floats for
|
408
|
+
# this setting of precision so that all method calls will
|
409
|
+
# automatically be invoked by Float-objects instead of R-objects.
|
410
|
+
# For this to work one needs
|
411
|
+
# require 'float_ext'
|
412
|
+
# and to use R.c instead of R.new.
|
413
|
+
|
414
|
+
def R.prec=(anInteger)
|
415
|
+
if anInteger.kind_of?(Numeric)
|
416
|
+
ai=anInteger.to_i # for robustness
|
417
|
+
ai = 0 unless ai.class == Fixnum
|
418
|
+
else
|
419
|
+
ai = 0
|
420
|
+
end
|
421
|
+
@@prec = ai > 0 ? ai : 0
|
422
|
+
if @@prec > 0
|
423
|
+
@@pi = BigMath::PI(@@prec)
|
424
|
+
@@e = BigMath::E(@@prec)
|
425
|
+
ln5 = BigMath.log(BigDecimal("5"),@@prec)
|
426
|
+
ln2 = BigMath.log(BigDecimal("2"),@@prec)
|
427
|
+
@@ln10 = ln5.add(ln2,@@prec)
|
428
|
+
@@lge = BigDecimal("1").div(@@ln10,@@prec)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
# Returns the number of digits.
|
433
|
+
# Usage:
|
434
|
+
# n = R.prec
|
435
|
+
def R.prec
|
436
|
+
return @@prec
|
437
|
+
end
|
438
|
+
|
439
|
+
# Changes the number of digits by adding the argument to the present
|
440
|
+
# value. The argument may be negative too, so that one easily sets the
|
441
|
+
# value back by means of the same function.
|
442
|
+
def R.add_prec(anInteger)
|
443
|
+
ai = anInteger.to_i
|
444
|
+
@@prec += ai
|
445
|
+
end
|
446
|
+
|
447
|
+
# Only for @@prec > 0, the function works, otherwise it fails.
|
448
|
+
# The argument is assumed to be either a BigDecimal, or any object x for
|
449
|
+
# which x.to_s determines a BigDecimal bd via bd = BigDecimal(x.to_s).
|
450
|
+
# This allows to input number-related character strings and numerical
|
451
|
+
# literals, and numberical variables in a variety of styles:
|
452
|
+
# i = 10000000
|
453
|
+
# j = 123456789
|
454
|
+
# a = R.new(i * j)
|
455
|
+
# b = R.new(1e-23)
|
456
|
+
# c = R.new("-117.07e99")
|
457
|
+
# d = R.new(1e66)
|
458
|
+
# e = R.new("1E666")
|
459
|
+
# The most universal mehod for generating large numbers is the one from
|
460
|
+
# String objects as in the definition of the variables c and e above.
|
461
|
+
# According to the design of BigDecimal, the exponent (given by the digits
|
462
|
+
# after the 'E') has to be representable by a Fixnum.
|
463
|
+
|
464
|
+
def initialize(aStringOrNumber="0.0")
|
465
|
+
fail "R.new makes sense only for @@prec > 0" unless @@prec > 0
|
466
|
+
if aStringOrNumber.kind_of?(BigDecimal)
|
467
|
+
@g = aStringOrNumber # in order to take the reuts from BigDecimal
|
468
|
+
# arithmetic directly, speeds programs up efficiently
|
469
|
+
else
|
470
|
+
x = BigDecimal(aStringOrNumber.to_s)
|
471
|
+
fail "Unable to define a BigDecimal from the argument" unless
|
472
|
+
x.kind_of?(BigDecimal)
|
473
|
+
@g = x
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
=begin
|
478
|
+
#old version, by a factor 3.65 slower in a typical test examle
|
479
|
+
def initialize(aStringOrNumber="0.0")
|
480
|
+
fail "R.new makes sense only for @@prec > 0" unless @@prec > 0
|
481
|
+
if aStringOrNumber.kind_of?(BigDecimal)
|
482
|
+
x = aStringOrNumber
|
483
|
+
else
|
484
|
+
x = BigDecimal(aStringOrNumber.to_s)
|
485
|
+
end
|
486
|
+
fail "Unable to define a BigDecimal from the argument" unless
|
487
|
+
x.kind_of?(BigDecimal)
|
488
|
+
if x.finite?
|
489
|
+
sp = x.split
|
490
|
+
res = sp[0] == -1 ? "-0." : "0."
|
491
|
+
core = sp[1].slice(0...@@prec)
|
492
|
+
res += core
|
493
|
+
res += "E"
|
494
|
+
res += sp[3].to_s
|
495
|
+
@g = BigDecimal(res)
|
496
|
+
else
|
497
|
+
@g = x
|
498
|
+
end
|
499
|
+
end
|
500
|
+
=end
|
501
|
+
|
502
|
+
# Number generator which yiels a Float for R.prec ==0 and a R else.
|
503
|
+
# This is the recommended method for introducing real numbers in
|
504
|
+
# programs since, when followed consequently, the whole program
|
505
|
+
# can be switched from Float precission to arbitrary precision
|
506
|
+
# by a single R.prec= statement. Usage:
|
507
|
+
# x = R.c("3.45E-45"); y = R.c 2; z = R.c(1.25e13); u = R.c "40.1e-1"
|
508
|
+
def R.c(aNumber)
|
509
|
+
if @@prec < 1 # yield a Float
|
510
|
+
nf = aNumber.to_f
|
511
|
+
if nf.class == Float
|
512
|
+
nf
|
513
|
+
else
|
514
|
+
nff = aNumber.to_s.to_f
|
515
|
+
if nff.class == Float
|
516
|
+
nff
|
517
|
+
else
|
518
|
+
fail "Unable to define a Float from the argument"
|
519
|
+
end
|
520
|
+
end
|
521
|
+
else # yield a R with the correct number of digits
|
522
|
+
R.new(aNumber)
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
# The clone has the required number of digits.
|
527
|
+
def clone; R.new(self); end
|
528
|
+
|
529
|
+
# The constant 0.
|
530
|
+
def R.zero
|
531
|
+
return 0.0 if @@prec < 1
|
532
|
+
R.new
|
533
|
+
end
|
534
|
+
# The constant 1.
|
535
|
+
def R.one
|
536
|
+
return 1.0 if @@prec < 1
|
537
|
+
R.new("1.0")
|
538
|
+
end
|
539
|
+
# The constant 2.
|
540
|
+
def R.two
|
541
|
+
return 2.0 if @@prec < 1
|
542
|
+
R.new("2.0")
|
543
|
+
end
|
544
|
+
# The constant 10.
|
545
|
+
def R.ten
|
546
|
+
return 10.0 if @@prec < 1
|
547
|
+
R.new("10.0")
|
548
|
+
end
|
549
|
+
# The constant 0.
|
550
|
+
def R.c0
|
551
|
+
return 0.0 if @@prec < 1
|
552
|
+
R.new
|
553
|
+
end
|
554
|
+
# The constant 1.
|
555
|
+
def R.c1
|
556
|
+
return 1.0 if @@prec < 1
|
557
|
+
R.new("1.0")
|
558
|
+
end
|
559
|
+
# The constant 2.
|
560
|
+
def R.c2
|
561
|
+
return 2.0 if @@prec < 1
|
562
|
+
R.new("2.0")
|
563
|
+
end
|
564
|
+
# The constant 3.
|
565
|
+
def R.c3
|
566
|
+
return 3.0 if @@prec < 1
|
567
|
+
R.new("3.0")
|
568
|
+
end
|
569
|
+
# The constant 4.
|
570
|
+
def R.c4
|
571
|
+
return 4.0 if @@prec < 1
|
572
|
+
R.new("4.0")
|
573
|
+
end
|
574
|
+
# The constant 5.
|
575
|
+
def R.c5
|
576
|
+
return 5.0 if @@prec < 1
|
577
|
+
R.new("5.0")
|
578
|
+
end
|
579
|
+
# The constant 6.
|
580
|
+
def R.c6
|
581
|
+
return 6.0 if @@prec < 1
|
582
|
+
R.new("6.0")
|
583
|
+
end
|
584
|
+
# The constant 7.
|
585
|
+
def R.c7
|
586
|
+
return 7.0 if @@prec < 1
|
587
|
+
R.new("7.0")
|
588
|
+
end
|
589
|
+
# The constant 8.
|
590
|
+
def R.c8
|
591
|
+
return 8.0 if @@prec < 1
|
592
|
+
R.new("8.0")
|
593
|
+
end
|
594
|
+
# The constant 9.
|
595
|
+
def R.c9
|
596
|
+
return 9.0 if @@prec < 1
|
597
|
+
R.new("9.0")
|
598
|
+
end
|
599
|
+
# The constant 10.
|
600
|
+
def R.c10
|
601
|
+
return 10.0 if @@prec < 1
|
602
|
+
R.new("10.0")
|
603
|
+
end
|
604
|
+
|
605
|
+
# The constant pi=3.14159...
|
606
|
+
def R.pi
|
607
|
+
return Math::PI if @@prec < 1
|
608
|
+
R.new(@@pi)
|
609
|
+
end
|
610
|
+
# The constant e = 2.718281828...
|
611
|
+
def R.e
|
612
|
+
return Math::E if @@prec < 1
|
613
|
+
R.new(@@e)
|
614
|
+
end
|
615
|
+
# The constant log10(e) = 0.43429448...
|
616
|
+
def R.lge
|
617
|
+
return Math::log10(Math::E) if @@prec < 1
|
618
|
+
R.new(@@lge)
|
619
|
+
end
|
620
|
+
# The constant log(10) = 2.30258...
|
621
|
+
def R.ln10
|
622
|
+
return Math::log(10.0) if @@prec < 1
|
623
|
+
R.new(@@ln10)
|
624
|
+
end
|
625
|
+
|
626
|
+
# The constant 1/2 (inverse 2)
|
627
|
+
def R.i2
|
628
|
+
return 0.5 if @@prec < 1
|
629
|
+
R.new("0.5")
|
630
|
+
end
|
631
|
+
# The constant 1/3.
|
632
|
+
def R.i3
|
633
|
+
R.c1/R.c3
|
634
|
+
end
|
635
|
+
# The constant 1/4.
|
636
|
+
def R.i4
|
637
|
+
return 0.25 if @@prec < 1
|
638
|
+
R.new("0.25")
|
639
|
+
end
|
640
|
+
# The constant 1/5.
|
641
|
+
def R.i5
|
642
|
+
return 0.2 if @@prec < 1
|
643
|
+
R.new("0.2")
|
644
|
+
end
|
645
|
+
# The constant 1/6.
|
646
|
+
def R.i6; R.c1/R.c6; end
|
647
|
+
# The constant 1/7.
|
648
|
+
def R.i7; R.c1/R.c7; end
|
649
|
+
# The constant 1/8.
|
650
|
+
def R.i8
|
651
|
+
return 0.125 if @@prec < 1
|
652
|
+
R.new("0.125")
|
653
|
+
end
|
654
|
+
# The constant 1/9.
|
655
|
+
def R.i9; R.c1/R.c9; end
|
656
|
+
# The constant 1/10.
|
657
|
+
def R.i10
|
658
|
+
return 0.1 if @@prec < 1
|
659
|
+
R.new("0.1")
|
660
|
+
end
|
661
|
+
# The constant NaN, not a number.
|
662
|
+
def R.nan
|
663
|
+
return 0.0/0.0 if @@prec < 1
|
664
|
+
R.new(BigDecimal("NaN"))
|
665
|
+
end
|
666
|
+
|
667
|
+
# Returns a two-component array containing the normalized
|
668
|
+
# fraction (an R) and the exponent (a Fixnum) of self.
|
669
|
+
# The exponent refers always to radix 10.
|
670
|
+
def frexp # member functions will be called only in situations in
|
671
|
+
# which @@prec > 0 is guarantied
|
672
|
+
n = @g.exponent
|
673
|
+
fac = BigDecimal("10").power(-n)
|
674
|
+
prel = [@g.mult(fac, @@prec), n]
|
675
|
+
[R.new(prel[0]),prel[1]]
|
676
|
+
end
|
677
|
+
|
678
|
+
# Adds the argument to the exponent of self.
|
679
|
+
# If self is a normalized fraction (and thus has exponent 0)
|
680
|
+
# the resulting number has an exponent given by the argument.
|
681
|
+
# This is the situation to which the functions name 'load exponent'
|
682
|
+
# refers.
|
683
|
+
def ldexp(anInteger)
|
684
|
+
ai=anInteger.to_i
|
685
|
+
self * (R.c10 ** ai)
|
686
|
+
end
|
687
|
+
|
688
|
+
# Random value (sine-floor random generator).
|
689
|
+
#
|
690
|
+
# Chaotic function from the integers into the
|
691
|
+
# unit interval [0,1] of R
|
692
|
+
# Argument condition: R.new(anInteger) defined
|
693
|
+
# Although the main intent is to use the function for integer
|
694
|
+
# argments, this is not essential.
|
695
|
+
|
696
|
+
def R.ran(anInteger)
|
697
|
+
if @@prec < 1 # no further usage of R
|
698
|
+
x = anInteger
|
699
|
+
y = 1e6 * Math::sin(x)
|
700
|
+
return y - y.floor
|
701
|
+
else
|
702
|
+
shift = 6
|
703
|
+
fac = R.one.ldexp(shift)
|
704
|
+
R.add_prec(shift)
|
705
|
+
x = R.c anInteger
|
706
|
+
y = fac * x.sin
|
707
|
+
res = (y - y.floor).round(@@prec - shift)
|
708
|
+
R.add_prec(-shift)
|
709
|
+
res
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
# Test object.
|
714
|
+
#
|
715
|
+
# Needed for automatic tests of arithmetic relations. Intended
|
716
|
+
# to give numbers which rapidly change sign and order of
|
717
|
+
# magnitude when the argument grows regularly e.g.
|
718
|
+
# as in 1,2,3,... . However, suitibility as a random generator is
|
719
|
+
# not the focus. If the second argument is 'true', the result
|
720
|
+
# is multplied by a number << 1 in order to prevent the result
|
721
|
+
# from overloading the exponential function.
|
722
|
+
|
723
|
+
def R.tob(anInteger, small = false)
|
724
|
+
small_a = small || @@prec<=0
|
725
|
+
ai=anInteger.to_i
|
726
|
+
mag_num1 = 7 # 'magic numbers'
|
727
|
+
mag_num2 = 11
|
728
|
+
mag_num3 = 17
|
729
|
+
mag_num4 = small_a ? 0.0423 : 1.7501
|
730
|
+
mag_num5 = small_a ? 0.13 : 0.65432
|
731
|
+
r1 = ai % mag_num1
|
732
|
+
r2 = ai % mag_num2
|
733
|
+
r3 = ai % mag_num3
|
734
|
+
y=(-r1 - r2 + r3) * mag_num4 + mag_num5
|
735
|
+
if @@prec < 1
|
736
|
+
res = Math::exp(y) * (ran(ai) - 0.5)
|
737
|
+
else
|
738
|
+
res = R.new(y).exp * (ran(ai) - R.i2)
|
739
|
+
end
|
740
|
+
end
|
741
|
+
|
742
|
+
# Returns the inverse of aNumber as a type controled by @@prec.
|
743
|
+
def R.i(aNumber)
|
744
|
+
if @@prec < 1
|
745
|
+
1.0/aNumber.to_f
|
746
|
+
else
|
747
|
+
R.c1/R.new(aNumber)
|
748
|
+
end
|
749
|
+
end
|
750
|
+
|
751
|
+
# Unary minus operator. It returns the R-object -self.
|
752
|
+
def -@; R.new(-@g); end
|
753
|
+
|
754
|
+
# Unary plus operator. It returns the R-object self.
|
755
|
+
def +@; R.new(@g); end
|
756
|
+
|
757
|
+
# (Complex) conjugation, no effect on real numbers.
|
758
|
+
# Supports the unified treatment of real and complex numbers.
|
759
|
+
def conj; self; end
|
760
|
+
|
761
|
+
# Redefining coerce from Numeric.
|
762
|
+
# This allows writing 1 + R.new(137) instead of R.new(137) + 1
|
763
|
+
# or R.new(137) + R.c1.
|
764
|
+
#-- Notice that the order in the resulting array is essential for
|
765
|
+
# correct functionality.
|
766
|
+
def coerce(a)
|
767
|
+
[ R.new(a), self]
|
768
|
+
end
|
769
|
+
|
770
|
+
# Adjust argument a so that its data type fits @@prec
|
771
|
+
|
772
|
+
def R.aa(a)
|
773
|
+
if a.class == R
|
774
|
+
a.g
|
775
|
+
else
|
776
|
+
BigDecimal(a.to_s)
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
# The basic order relation.
|
781
|
+
def <=>(a)
|
782
|
+
@g <=> R.aa(a)
|
783
|
+
end
|
784
|
+
|
785
|
+
# Returns 'true' if self equals zero.
|
786
|
+
def zero?; @g.zero?; end
|
787
|
+
|
788
|
+
# Returns 'true' if self is 'not a number' (NaN).
|
789
|
+
def nan?; @g.nan?; end
|
790
|
+
|
791
|
+
def infinite?; @g.infinite?; end
|
792
|
+
|
793
|
+
# Since R is not Fixnum or Bignum we return 'false'. In scientific
|
794
|
+
# computation there may be the need to use various types of 'real number
|
795
|
+
# types' but there should be always a clear-cut distinction between
|
796
|
+
# integer types and real types.
|
797
|
+
def integer?; false; end
|
798
|
+
|
799
|
+
# Supports the unified treatment of real and complex numbers.
|
800
|
+
def real?; true; end
|
801
|
+
|
802
|
+
# Supports the unified treatment of real and complex numbers.
|
803
|
+
def complex?; false; end
|
804
|
+
|
805
|
+
# Returns the R-object self + aR.
|
806
|
+
def +(aR)
|
807
|
+
R.new(@g.add(R.aa(aR),@@prec))
|
808
|
+
end
|
809
|
+
|
810
|
+
# Returns the R-object self - aR.
|
811
|
+
def -(aR)
|
812
|
+
R.new(@g.sub(R.aa(aR),@@prec))
|
813
|
+
end
|
814
|
+
|
815
|
+
# Returns the R-object self * aR.
|
816
|
+
def *(aR)
|
817
|
+
R.new(@g.mult(R.aa(aR),@@prec))
|
818
|
+
end
|
819
|
+
|
820
|
+
# Returns the R-object self / aR.
|
821
|
+
def /(aR)
|
822
|
+
R.new(@g.div(R.aa(aR),@@prec))
|
823
|
+
end
|
824
|
+
|
825
|
+
# Usual modulo division.
|
826
|
+
def %(aR)
|
827
|
+
R.new(@g%R.aa(aR))
|
828
|
+
end
|
829
|
+
|
830
|
+
# Returns the a-th power of self, if a is an integer argument,
|
831
|
+
# and the a-th power of self.abs for real, non-integer a.
|
832
|
+
# We put 0**a = 0 for all non-integer a.
|
833
|
+
#-- If a is integer, the functionality is as given by classes Float and
|
834
|
+
# BigDecimal. For non-integer a, we take the freedom do make the definition 0
|
835
|
+
# where mathematics suggests complex (e.g. (-1)**0.5) or infinite ( e.g. 0**-1)
|
836
|
+
# results.
|
837
|
+
def **(a)
|
838
|
+
return R.nan if nan?
|
839
|
+
if a.class == Fixnum || a.class == Bignum
|
840
|
+
return R.new(@g.power(a))
|
841
|
+
end
|
842
|
+
a = R.new(a) if a.class != R
|
843
|
+
R.nan if a.nan?
|
844
|
+
x = abs
|
845
|
+
if x.zero?
|
846
|
+
R.c0
|
847
|
+
else
|
848
|
+
(x.log * a).exp
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
# Returns the zero-element which belongs to the same class than self
|
853
|
+
def to_0; R.c0; end
|
854
|
+
|
855
|
+
# Returns the unit-element which belongs to the same class than self
|
856
|
+
def to_1; R.c1; end
|
857
|
+
|
858
|
+
# Returns the inverse 1/self.
|
859
|
+
def inv
|
860
|
+
return R.nan if nan?
|
861
|
+
R.new(BigDecimal("1").div(@g,@@prec))
|
862
|
+
end
|
863
|
+
|
864
|
+
# The pseudo inverse is always defined: the pseudo inverse of 0 is 0.
|
865
|
+
def pseudo_inv
|
866
|
+
return R.c0 if zero?
|
867
|
+
inv
|
868
|
+
end
|
869
|
+
|
870
|
+
# If the method gets no argument we return the 'nearest integer':
|
871
|
+
# For the return value res we have
|
872
|
+
# res.int? == true
|
873
|
+
# and
|
874
|
+
# (self - res).abs <= 0.5
|
875
|
+
# For an integer argument we return
|
876
|
+
# a real number, the significand of which has not more
|
877
|
+
# than n digits. Notice that there is also a function.
|
878
|
+
def round(*arg)
|
879
|
+
n = arg.size
|
880
|
+
case n
|
881
|
+
when 0
|
882
|
+
(self + 0.5).floor.to_i # here we ask for an integer output
|
883
|
+
# notice that R#round maps to R
|
884
|
+
when 1
|
885
|
+
m = arg[0].to_i
|
886
|
+
x = frexp
|
887
|
+
y = x[0].ldexp(m)
|
888
|
+
(y + 0.5).floor.ldexp(x[1] - m)
|
889
|
+
else
|
890
|
+
fail "needs 0 or 1 arguments"
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
# Returns the square root of self.
|
895
|
+
def sqrt
|
896
|
+
return R.nan if nan?
|
897
|
+
R.new(@g.sqrt(@@prec))
|
898
|
+
end
|
899
|
+
|
900
|
+
# Returns the absolute value of self.
|
901
|
+
def abs; R.new(@g.abs); end
|
902
|
+
|
903
|
+
# Returns the square of the absolute value of self.
|
904
|
+
def abs2; self * self; end
|
905
|
+
|
906
|
+
# Returns a kind of relative distance between self and aR.
|
907
|
+
# The return value varies from 0 to 1, where 1 means maximum dissimilarity
|
908
|
+
# of the arguments.
|
909
|
+
# Such a function is needed for testing the validity of arithmetic laws,
|
910
|
+
# which, due to numerical noise, should not be expected to be fulfilled
|
911
|
+
# exactly.
|
912
|
+
def dis(aR)
|
913
|
+
aR = R.aa(aR)
|
914
|
+
a = self.abs
|
915
|
+
b = aR.abs
|
916
|
+
d = (self - aR).abs
|
917
|
+
s = a + b
|
918
|
+
return R.c0 if s.zero?
|
919
|
+
d1 = d/s
|
920
|
+
d < d1 ? d : d1
|
921
|
+
end
|
922
|
+
|
923
|
+
# Returns the floor value of self (the largest integer R <= self).
|
924
|
+
def floor; R.new(@g.floor); end
|
925
|
+
|
926
|
+
# Returns the ceil value of self (the smallest integer R >= self).
|
927
|
+
def ceil; R.new(@g.ceil); end
|
928
|
+
|
929
|
+
# Conversion to String.
|
930
|
+
def to_s; @g.to_s; end
|
931
|
+
|
932
|
+
# Printing the value together with a label
|
933
|
+
def prn(name)
|
934
|
+
puts "#{name} = " + to_s
|
935
|
+
end
|
936
|
+
|
937
|
+
# Conversion to integer.
|
938
|
+
def to_i; @g.to_i; end
|
939
|
+
|
940
|
+
# Conversion to integer.
|
941
|
+
def to_int; @g.to_int; end
|
942
|
+
|
943
|
+
# Conversion to double.
|
944
|
+
def to_f
|
945
|
+
@g.to_f
|
946
|
+
end
|
947
|
+
|
948
|
+
# Auxliar version of arcsin ( not public).
|
949
|
+
#
|
950
|
+
# Definition in terms of a power series. The convergence becomes very bad when
|
951
|
+
# self.abs becomes close to 1. The present function will actually be used for
|
952
|
+
# defining atan.
|
953
|
+
|
954
|
+
def asin_aux
|
955
|
+
return R.nan if @g.infinite? || @g.nan?
|
956
|
+
one=BigDecimal("1")
|
957
|
+
raise ArgumentError, "@g.abs must be <= 1.0" if @g.abs>one
|
958
|
+
n = @@prec + BigDecimal.double_fig
|
959
|
+
y = @g
|
960
|
+
d = y
|
961
|
+
t = @g
|
962
|
+
n1 = one
|
963
|
+
n2 = BigDecimal("2")
|
964
|
+
n3 = BigDecimal("3")
|
965
|
+
x2 = @g.mult(@g,n)
|
966
|
+
while d.nonzero? && ((m = n - (y.exponent - d.exponent).abs) > 0)
|
967
|
+
m = BigDecimal.double_fig if m < BigDecimal.double_fig
|
968
|
+
t = t.mult(x2,n)*n1/n2
|
969
|
+
d = t.div(n3,m)
|
970
|
+
y += d
|
971
|
+
n1 += 2
|
972
|
+
n2 += 2
|
973
|
+
n3 += 2
|
974
|
+
end
|
975
|
+
R.new(y)
|
976
|
+
end
|
977
|
+
|
978
|
+
# Auxliar version of arctan ( not public).
|
979
|
+
def atan_aux
|
980
|
+
a = @g.abs
|
981
|
+
if a < BigDecimal("1.618")
|
982
|
+
x = self / (R.c1+self*self).sqrt # x.abs < 1/1.618 = 0.618
|
983
|
+
y = x.asin_aux
|
984
|
+
else
|
985
|
+
rec = BigDecimal("1").div(@g,@@prec) # rec.abs < 1/1.618 = 0.618
|
986
|
+
y = R.pi * R.i2 - R.new(BigMath.atan(rec,@@prec))
|
987
|
+
end
|
988
|
+
end
|
989
|
+
|
990
|
+
# Argument, i.e. polar angle phi of point (x=self,y), -pi < phi <= pi.
|
991
|
+
#
|
992
|
+
# This is the basic tool for defining asin, acos, atan, acot.
|
993
|
+
# Notice x.arg(y) == y.atan2(x) with function atan2 to be defined next.
|
994
|
+
def arg(y)
|
995
|
+
a=(self*self+y*y).sqrt
|
996
|
+
res = R.c2*(y/(a+self)).atan_aux
|
997
|
+
res -= R.pi * 2 if res > R.pi
|
998
|
+
res
|
999
|
+
end
|
1000
|
+
|
1001
|
+
# The value y.atan2(x) is the polar angle of point (x,y) and corresponds
|
1002
|
+
# to Math.atan2(y,x), where the squeere order of arguments is the same
|
1003
|
+
# as in the (poor) formula atan(y/x).
|
1004
|
+
def atan2(x); x.arg(self); end
|
1005
|
+
|
1006
|
+
# Returns he hypotenuse of a right-angled triangle with
|
1007
|
+
# sides x = self and y.
|
1008
|
+
def hypot(y); (self * self + y * y).sqrt; end
|
1009
|
+
|
1010
|
+
# Inverse sine.
|
1011
|
+
def asin
|
1012
|
+
y = self
|
1013
|
+
x = (R.c1 - y * y).sqrt
|
1014
|
+
x.arg(y)
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# Inverse cosine.
|
1018
|
+
def acos
|
1019
|
+
x = self
|
1020
|
+
y = (R.c1 - x * x).sqrt
|
1021
|
+
x.arg(y)
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
# Inverse tangent.
|
1025
|
+
def atan
|
1026
|
+
cosine = (R.c1 + self * self).sqrt.inv
|
1027
|
+
sine = cosine * self
|
1028
|
+
cosine.arg(sine)
|
1029
|
+
end
|
1030
|
+
|
1031
|
+
# Inverse cotangent.
|
1032
|
+
def acot
|
1033
|
+
a = inv
|
1034
|
+
a.atan
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# Sine.
|
1038
|
+
#
|
1039
|
+
# This reduces computation of the sine of any angle
|
1040
|
+
# to computation of sine or cosine of a angle less than pi/4
|
1041
|
+
# and thus less than 1. This speeds up the convergence of the
|
1042
|
+
# sine or cosine power series.
|
1043
|
+
|
1044
|
+
def sin
|
1045
|
+
return R.nan if @g.infinite? || @g.nan?
|
1046
|
+
sign=1
|
1047
|
+
if @g >= BigDecimal("0")
|
1048
|
+
x=@g
|
1049
|
+
else
|
1050
|
+
x=-@g
|
1051
|
+
sign=-1
|
1052
|
+
end
|
1053
|
+
twoPi=@@pi*BigDecimal("2")
|
1054
|
+
piHalf=@@pi*BigDecimal("0.5")
|
1055
|
+
piQuart=@@pi*BigDecimal("0.25")
|
1056
|
+
res=x.divmod(twoPi)
|
1057
|
+
phi=res[1]
|
1058
|
+
res2=phi.divmod(piHalf)
|
1059
|
+
qn=res2[0] # number of quadrant 0,1,2,3
|
1060
|
+
alpha=res2[1] # angle in quadrant
|
1061
|
+
if qn==0
|
1062
|
+
# first quadrant
|
1063
|
+
if alpha<=piQuart
|
1064
|
+
r=R.new(BigMath.sin(alpha,@@prec))
|
1065
|
+
else
|
1066
|
+
r=R.new(BigMath.cos(piHalf-alpha,@@prec))
|
1067
|
+
end
|
1068
|
+
elsif qn==1
|
1069
|
+
# second quadrant
|
1070
|
+
if alpha<=piQuart
|
1071
|
+
r=R.new(BigMath.cos(alpha,@@prec))
|
1072
|
+
else
|
1073
|
+
r=R.new(BigMath.sin(piHalf-alpha,@@prec))
|
1074
|
+
end
|
1075
|
+
elsif qn==2
|
1076
|
+
# third quadrant
|
1077
|
+
if alpha<=piQuart
|
1078
|
+
r=-R.new(BigMath.sin(alpha,@@prec))
|
1079
|
+
else
|
1080
|
+
r=-R.new(BigMath.cos(piHalf-alpha,@@prec))
|
1081
|
+
end
|
1082
|
+
elsif qn==3
|
1083
|
+
# fourth quadrant
|
1084
|
+
if alpha<=piQuart
|
1085
|
+
r=-R.new(BigMath.cos(alpha,@@prec))
|
1086
|
+
else
|
1087
|
+
r=-R.new(BigMath.sin(piHalf-alpha,@@prec))
|
1088
|
+
end
|
1089
|
+
else
|
1090
|
+
puts "error in R.sin, not assumed to happen"
|
1091
|
+
end
|
1092
|
+
sign == -1 ? -r : r
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
# Cosine.
|
1096
|
+
def cos
|
1097
|
+
piHalf=R.pi*R.new("0.5")
|
1098
|
+
(self+piHalf).sin
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
# Tangent.
|
1102
|
+
def tan
|
1103
|
+
self.sin/self.cos
|
1104
|
+
end
|
1105
|
+
|
1106
|
+
# Cotangent.
|
1107
|
+
def cot
|
1108
|
+
self.cos/self.sin
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
# Power of 10: exp10(x) = 10**x.
|
1112
|
+
# Auxiliar function for the implementation of exp.
|
1113
|
+
#-- For decimal numbers this can efficiently be reduced to
|
1114
|
+
# smaller numbers
|
1115
|
+
def exp10
|
1116
|
+
return R.nan if @g.infinite? || @g.nan?
|
1117
|
+
n = @g.floor
|
1118
|
+
f = @g - n
|
1119
|
+
x = BigMath.exp(f.mult(@@ln10,@@prec),@@prec)
|
1120
|
+
newExp=n.to_i # newExp has to be a Fixnum in order that
|
1121
|
+
# the next line defines a BigDecimal. This looks like a missed
|
1122
|
+
# opportunity: numbers that accept Bignums as exponents should
|
1123
|
+
# be not much more difficult to implement.
|
1124
|
+
p10=BigDecimal("1.0E"+newExp.to_s) # a power of 10, generated on the
|
1125
|
+
# level of symbols, not by computation
|
1126
|
+
y = x.mult(p10,@@prec)
|
1127
|
+
R.new(y)
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
# Exponential function.
|
1131
|
+
def exp
|
1132
|
+
(self * R.lge).exp10
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
# Hyperbolic sine.
|
1136
|
+
def sinh; (self.exp - (-self).exp) * R.i2; end
|
1137
|
+
|
1138
|
+
# Hyperbolic cosine.
|
1139
|
+
def cosh; (self.exp + (-self).exp) * R.i2; end
|
1140
|
+
|
1141
|
+
# Hyperbolic tangent.
|
1142
|
+
def tanh
|
1143
|
+
s = self.exp - (-self).exp
|
1144
|
+
c = self.exp + (-self).exp
|
1145
|
+
s/c
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
# Hyperbolic cotangent.
|
1149
|
+
def coth
|
1150
|
+
s = self.exp - (-self).exp
|
1151
|
+
c = self.exp + (-self).exp
|
1152
|
+
c/s
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
# Logarithm to base 10.
|
1156
|
+
#
|
1157
|
+
# Makes use of knowing the decimal exponent
|
1158
|
+
# for a reduction to an actually small argument.
|
1159
|
+
# We understand log10(x) as log10(|x|), which is
|
1160
|
+
# natural in the real domain.
|
1161
|
+
|
1162
|
+
def log10
|
1163
|
+
return R.nan if nan?
|
1164
|
+
return R.nan if @g.infinite? || @g.nan?
|
1165
|
+
sp=@g.split
|
1166
|
+
exponent=sp[3]
|
1167
|
+
s="0."+sp[1]
|
1168
|
+
x=BigDecimal(s)
|
1169
|
+
if x.zero?
|
1170
|
+
fail "zero argument of log"
|
1171
|
+
end
|
1172
|
+
x=BigMath.log(x.abs,@@prec)
|
1173
|
+
y=BigDecimal(exponent.to_s)
|
1174
|
+
R.new(x) * R.new(@@lge) + R.new(y)
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
# Natural logarithm.
|
1178
|
+
def log
|
1179
|
+
return R.nan if nan?
|
1180
|
+
r1=self.log10
|
1181
|
+
r2=R.e.log10
|
1182
|
+
r1/r2
|
1183
|
+
end
|
1184
|
+
|
1185
|
+
# Inverse hyperbolic sine.
|
1186
|
+
def asinh
|
1187
|
+
((self * self + R.c1).sqrt + self).log
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
# Inverse hyperbolic cosine.
|
1191
|
+
def acosh
|
1192
|
+
((self * self - R.c1).sqrt + self).abs.log
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
# Inverse hyperbolic tangent.
|
1196
|
+
def atanh
|
1197
|
+
((R.c1 + self)/(R.c1 - self)).abs.log * R.i2
|
1198
|
+
end
|
1199
|
+
|
1200
|
+
# Inverse hyperbolic cotangent.
|
1201
|
+
def acoth
|
1202
|
+
((self + R.c1)/(self - R.c1)).abs.log * R.i2
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# Returns the value of erfc for large arguments according to the
|
1206
|
+
# asymptotic formla 7.1.23 on p. 298 of Abramowitz Stegun.
|
1207
|
+
# From the sum over m = 1, 2, ... we take the terms up to m = 4.
|
1208
|
+
def erfc_asy
|
1209
|
+
z = self *self
|
1210
|
+
z2 = z * R.c2
|
1211
|
+
yp = z2.clone
|
1212
|
+
s = R.c1 - R.c1/yp
|
1213
|
+
yp *= z2
|
1214
|
+
s += R.c3/yp
|
1215
|
+
yp *= z2
|
1216
|
+
s -= R.new("15")/yp
|
1217
|
+
yp *= z2
|
1218
|
+
s += R.new("105")/yp
|
1219
|
+
yp *= z2
|
1220
|
+
s -= R.new("945")/yp
|
1221
|
+
(-z).exp * s/R.pi.sqrt
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
# Returns the value obtained from evaluating the power series of erf
|
1225
|
+
# at x = self. Here it is assumed, that x < 9 which is sufficient since
|
1226
|
+
# for larger values we have a asymptotic formula. If we had to use larger
|
1227
|
+
# x's, the 'magic number' 33 in the code had to be increased, which would
|
1228
|
+
# slow down the computation.
|
1229
|
+
#-- Source: Abramowitz Stegun p. 297, formula 7.1.5 .
|
1230
|
+
def erf_ps
|
1231
|
+
return R.nan if @g.infinite? || @g.nan?
|
1232
|
+
x = self
|
1233
|
+
n = @@prec + BigDecimal.double_fig
|
1234
|
+
x2 = -x * x
|
1235
|
+
y = R.c1
|
1236
|
+
i = R.c0
|
1237
|
+
b = R.c1
|
1238
|
+
c = R.c1
|
1239
|
+
a = R.c0
|
1240
|
+
n_add = 33
|
1241
|
+
R.add_prec(n_add)
|
1242
|
+
while a.frexp[1] > y.frexp[1] - n
|
1243
|
+
i += R.c1
|
1244
|
+
b *= x2/i
|
1245
|
+
c += R.c2
|
1246
|
+
a = b/c
|
1247
|
+
y += a
|
1248
|
+
end
|
1249
|
+
R.add_prec(-n_add)
|
1250
|
+
res = y * R.c2 * x / R.pi.sqrt
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
# Returns the error function evaluated at x=self.
|
1254
|
+
def erf
|
1255
|
+
if self < R.c0
|
1256
|
+
x = abs
|
1257
|
+
sig = - R.c1
|
1258
|
+
else
|
1259
|
+
x = self
|
1260
|
+
sig = R.c1
|
1261
|
+
end
|
1262
|
+
if x > R.c9
|
1263
|
+
return (R.c1 - x.erfc_asy)*sig
|
1264
|
+
else
|
1265
|
+
return x.erf_ps * sig
|
1266
|
+
end
|
1267
|
+
end
|
1268
|
+
|
1269
|
+
# Returns the complementary error function erfc evaluated at x=self.
|
1270
|
+
def erfc
|
1271
|
+
if self > R.c9
|
1272
|
+
erfc_asy
|
1273
|
+
else
|
1274
|
+
R.c1 - erf
|
1275
|
+
end
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
# Consistency test for class R
|
1279
|
+
# This is intended to keep the class consistent despite of modifications.
|
1280
|
+
# The first argument influences the numbers which are selected for the
|
1281
|
+
# test. Returned is a sum of numbers each of which should be numerical
|
1282
|
+
# noise and so the result has to be << 1 if the test is to indicate
|
1283
|
+
# success.
|
1284
|
+
# For istance, on my system
|
1285
|
+
# R.prec = 100; R.test(137)
|
1286
|
+
# produces
|
1287
|
+
# The error sum is 0.1654782936431420775338085739363521532600906524358
|
1288
|
+
# 495733897874347055126769599726793415224324363846749E-29 .
|
1289
|
+
# Computation time was 0.853 seconds.
|
1290
|
+
|
1291
|
+
def R.test(n0, verbose = false )
|
1292
|
+
puts "Doing R.test(#{n0}, #{verbose}) for R.prec = #{@@prec}:"
|
1293
|
+
puts "*************************************************"
|
1294
|
+
require 'float_ext'
|
1295
|
+
t1 = Time.now
|
1296
|
+
small = R.prec <= 0
|
1297
|
+
s = R.c0
|
1298
|
+
puts "class of s is " + s.class.to_s
|
1299
|
+
i = n0
|
1300
|
+
a = R.tob(i)
|
1301
|
+
i += 1
|
1302
|
+
b = R.tob(i)
|
1303
|
+
i += 1
|
1304
|
+
c = R.tob(i)
|
1305
|
+
i += 1
|
1306
|
+
|
1307
|
+
r = 2 + a
|
1308
|
+
l = R.c2 + a
|
1309
|
+
ds = r.dis(l)
|
1310
|
+
puts "coerce 2 + a: ds = " + ds.to_s if verbose
|
1311
|
+
s += ds
|
1312
|
+
|
1313
|
+
r = a + 1.234
|
1314
|
+
l = a + R.c(1.234)
|
1315
|
+
ds = r.dis(l)
|
1316
|
+
puts "coerce a + float: ds = " + ds.to_s if verbose
|
1317
|
+
s += ds
|
1318
|
+
|
1319
|
+
ai = a.round
|
1320
|
+
bool_val = ai.integer?
|
1321
|
+
|
1322
|
+
ds = bool_val ? 0 : 1
|
1323
|
+
puts "rounding gives integer type: ds = " + ds.to_s if verbose
|
1324
|
+
s += ds
|
1325
|
+
|
1326
|
+
diff = (a - ai).abs
|
1327
|
+
bool_val = diff <= 0.5
|
1328
|
+
ds = bool_val ? 0 : 1
|
1329
|
+
puts "rounding is accurate: ds = " + ds.to_s if verbose
|
1330
|
+
s += ds
|
1331
|
+
|
1332
|
+
r = (a + b) * c
|
1333
|
+
l = a * c + b * c
|
1334
|
+
|
1335
|
+
ds = r.dis(l)
|
1336
|
+
puts "Distributive law for +: ds = " + ds.to_s if verbose
|
1337
|
+
s += ds
|
1338
|
+
|
1339
|
+
r = (a - b) * c
|
1340
|
+
l = a * c - b * c
|
1341
|
+
ds = r.dis(l)
|
1342
|
+
puts "Distributive law for -: ds = " + ds.to_s if verbose
|
1343
|
+
s += ds
|
1344
|
+
|
1345
|
+
r = (a * b) * c
|
1346
|
+
l = b * (c * a)
|
1347
|
+
ds = r.dis(l)
|
1348
|
+
puts "Multiplication: ds = " + ds.to_s if verbose
|
1349
|
+
s += ds
|
1350
|
+
|
1351
|
+
a = R.tob(i)
|
1352
|
+
i += 1
|
1353
|
+
b = R.tob(i)
|
1354
|
+
i += 1
|
1355
|
+
c = R.tob(i)
|
1356
|
+
i += 1
|
1357
|
+
|
1358
|
+
r = (a * b) / c
|
1359
|
+
l = (a / c) * b
|
1360
|
+
ds = r.dis(l)
|
1361
|
+
puts "Division: ds = " + ds.to_s if verbose
|
1362
|
+
s += ds
|
1363
|
+
|
1364
|
+
x = R.c0/R.c0
|
1365
|
+
y = x.nan?
|
1366
|
+
ds = y ? 0 : 1
|
1367
|
+
puts "0/0: ds = " + ds.to_s if verbose
|
1368
|
+
s += ds
|
1369
|
+
|
1370
|
+
r = R.c1
|
1371
|
+
l = a * a.inv
|
1372
|
+
ds = r.dis(l)
|
1373
|
+
puts "inv: ds = " + ds.to_s if verbose
|
1374
|
+
s += ds
|
1375
|
+
|
1376
|
+
|
1377
|
+
r = 1/a
|
1378
|
+
l = a.inv
|
1379
|
+
ds = r.dis(l)
|
1380
|
+
puts "inv and 1/x: ds = " + ds.to_s if verbose
|
1381
|
+
s += ds
|
1382
|
+
|
1383
|
+
r = b
|
1384
|
+
l = -(-b)
|
1385
|
+
ds = r.dis(l)
|
1386
|
+
puts "Unary minus is idempotent: ds = " + ds.to_s if verbose
|
1387
|
+
s += ds
|
1388
|
+
|
1389
|
+
x = -a
|
1390
|
+
y = x + a
|
1391
|
+
r = y
|
1392
|
+
l = R.c0
|
1393
|
+
ds = r.dis(l)
|
1394
|
+
puts "Unary -: ds = " + ds.to_s if verbose
|
1395
|
+
s += ds
|
1396
|
+
|
1397
|
+
l = a.sin * b.cos + a.cos * b.sin
|
1398
|
+
r = (a + b).sin
|
1399
|
+
ds = r.dis(l)
|
1400
|
+
puts "Addition theorem for sin: ds = " + ds.to_s if verbose
|
1401
|
+
s += ds
|
1402
|
+
|
1403
|
+
l = a.sin ** 2 + a.cos ** 2
|
1404
|
+
r = R.c1
|
1405
|
+
ds = r.dis(l)
|
1406
|
+
puts "sin^2 + cos^2: ds = " + ds.to_s if verbose
|
1407
|
+
s += ds
|
1408
|
+
|
1409
|
+
x = a.sin
|
1410
|
+
y = a.cos
|
1411
|
+
l = x.hypot(y)
|
1412
|
+
r = R.c1
|
1413
|
+
ds = r.dis(l)
|
1414
|
+
puts "hypot: ds = " + ds.to_s if verbose
|
1415
|
+
s += ds
|
1416
|
+
|
1417
|
+
phi = (R.ran(i) - R.i2) * R.pi * 2
|
1418
|
+
x = phi.cos
|
1419
|
+
y = phi.sin
|
1420
|
+
r = phi
|
1421
|
+
l = x.arg(y)
|
1422
|
+
ds = r.dis(l)
|
1423
|
+
puts "arg: ds = " + ds.to_s if verbose
|
1424
|
+
s += ds
|
1425
|
+
|
1426
|
+
l = a.exp * b.exp
|
1427
|
+
r = (a + b).exp
|
1428
|
+
ds = r.dis(l)
|
1429
|
+
puts "Addition theorem for exp: ds = " + ds.to_s if verbose
|
1430
|
+
s += ds
|
1431
|
+
|
1432
|
+
l = b
|
1433
|
+
r = b.exp.log
|
1434
|
+
ds = r.dis(l)
|
1435
|
+
puts "exp and log: ds = " + ds.to_s if verbose
|
1436
|
+
s += ds
|
1437
|
+
|
1438
|
+
x = c.abs
|
1439
|
+
l = x
|
1440
|
+
r = x.log.exp
|
1441
|
+
ds = r.dis(l)
|
1442
|
+
puts "log and exp: ds = " + ds.to_s if verbose
|
1443
|
+
s += ds
|
1444
|
+
|
1445
|
+
i +=1
|
1446
|
+
a = R.tob(i)
|
1447
|
+
l = a.sin
|
1448
|
+
r = l.asin.sin
|
1449
|
+
ds = r.dis(l)
|
1450
|
+
puts "asin and sin: ds = " + ds.to_s if verbose
|
1451
|
+
s += ds
|
1452
|
+
|
1453
|
+
i +=1
|
1454
|
+
a = R.tob(i)
|
1455
|
+
l = a.cos
|
1456
|
+
r = l.acos.cos
|
1457
|
+
ds = r.dis(l)
|
1458
|
+
puts "acos and cos: ds = " + ds.to_s if verbose
|
1459
|
+
s += ds
|
1460
|
+
|
1461
|
+
i +=1
|
1462
|
+
a = R.tob(i)
|
1463
|
+
l = a.tan
|
1464
|
+
r = l.atan.tan
|
1465
|
+
ds = r.dis(l)
|
1466
|
+
puts "atan and tan: ds = " + ds.to_s if verbose
|
1467
|
+
s += ds
|
1468
|
+
|
1469
|
+
i +=1
|
1470
|
+
a = R.tob(i)
|
1471
|
+
l = a.cot
|
1472
|
+
r = l.acot.cot
|
1473
|
+
ds = r.dis(l)
|
1474
|
+
puts "acot and cot: ds = " + ds.to_s if verbose
|
1475
|
+
s += ds
|
1476
|
+
|
1477
|
+
i +=1
|
1478
|
+
a = R.tob(i,true) # smaller version, in order
|
1479
|
+
# not to overload function exp
|
1480
|
+
l = a.sinh
|
1481
|
+
r = l.asinh.sinh
|
1482
|
+
ds = r.dis(l)
|
1483
|
+
puts "asinh and sinh: ds = " + ds.to_s if verbose
|
1484
|
+
s += ds
|
1485
|
+
|
1486
|
+
i +=1
|
1487
|
+
a = R.tob(i,true)
|
1488
|
+
l = a.cosh
|
1489
|
+
r = l.acosh.cosh
|
1490
|
+
ds = r.dis(l)
|
1491
|
+
puts "acosh and cosh: ds = " + ds.to_s if verbose
|
1492
|
+
s += ds
|
1493
|
+
|
1494
|
+
i +=1
|
1495
|
+
a = R.tob(i,true)
|
1496
|
+
l = a.tanh
|
1497
|
+
r = l.atanh.tanh
|
1498
|
+
ds = r.dis(l)
|
1499
|
+
puts "atanh and tanh: ds = " + ds.to_s if verbose
|
1500
|
+
s += ds
|
1501
|
+
|
1502
|
+
i +=1
|
1503
|
+
a = R.tob(i,true)
|
1504
|
+
l = a.coth
|
1505
|
+
r = l.acoth.coth
|
1506
|
+
ds = r.dis(l)
|
1507
|
+
puts "acoth and coth: ds = " + ds.to_s if verbose
|
1508
|
+
s += ds
|
1509
|
+
|
1510
|
+
i += 1
|
1511
|
+
a = R.tob(i,true)
|
1512
|
+
i += 1
|
1513
|
+
b = R.tob(i,true)
|
1514
|
+
i += 1
|
1515
|
+
c = R.tob(i,true)
|
1516
|
+
i += 1
|
1517
|
+
|
1518
|
+
ap = a.abs
|
1519
|
+
l = (ap ** b) ** c
|
1520
|
+
r = ap ** (b * c)
|
1521
|
+
ds = r.dis(l)
|
1522
|
+
"general power: ds = " + ds.to_s if verbose
|
1523
|
+
s += ds
|
1524
|
+
|
1525
|
+
l = (ap ** b) * (ap ** c)
|
1526
|
+
r = ap ** (b + c)
|
1527
|
+
ds = r.dis(l)
|
1528
|
+
puts "general power, addition theorem: ds = " + ds.to_s if verbose
|
1529
|
+
s += ds
|
1530
|
+
|
1531
|
+
x=(a.abs+b.abs+c.abs)
|
1532
|
+
l = x.sqrt
|
1533
|
+
r = x ** 0.5
|
1534
|
+
ds = r.dis(l)
|
1535
|
+
puts "square root as power: ds = " + ds.to_s if verbose
|
1536
|
+
s += ds
|
1537
|
+
|
1538
|
+
bi = i % 11 -6
|
1539
|
+
ci = i % 7 - 3
|
1540
|
+
bi = 7 if bi.zero?
|
1541
|
+
ci = 3 if ci.zero?
|
1542
|
+
# avoid trivial 0
|
1543
|
+
l = (a ** bi) ** ci
|
1544
|
+
r = a ** (bi * ci)
|
1545
|
+
puts "bi = " + bi.to_s + " ci = " + ci.to_s if verbose
|
1546
|
+
ds = r.dis(l)
|
1547
|
+
puts "integer power: ds = " + ds.to_s if verbose
|
1548
|
+
s += ds
|
1549
|
+
|
1550
|
+
r = b
|
1551
|
+
l = b.clone
|
1552
|
+
ds = r.dis(l)
|
1553
|
+
puts "cloning: ds = " + ds.to_s if verbose
|
1554
|
+
s += ds
|
1555
|
+
ds = (l == r ? 0.0 : 1.0)
|
1556
|
+
puts "cloning and ==: ds = " + ds.to_s if verbose
|
1557
|
+
x = a
|
1558
|
+
y = b
|
1559
|
+
p, q = x.divmod(y)
|
1560
|
+
l = x
|
1561
|
+
r = y * p + q
|
1562
|
+
ds = r.dis(l)
|
1563
|
+
puts "divmod 1: ds = " + ds.to_s if verbose
|
1564
|
+
s += ds
|
1565
|
+
|
1566
|
+
x = a
|
1567
|
+
y = -b
|
1568
|
+
p, q = x.divmod(y)
|
1569
|
+
l = x
|
1570
|
+
r = y * p + q
|
1571
|
+
ds = r.dis(l)
|
1572
|
+
puts "divmod 2: ds = " + ds.to_s if verbose
|
1573
|
+
s += ds
|
1574
|
+
|
1575
|
+
x = b
|
1576
|
+
y = a
|
1577
|
+
p, q = x.divmod(y)
|
1578
|
+
l = x
|
1579
|
+
r = y * p + q
|
1580
|
+
ds = r.dis(l)
|
1581
|
+
puts "divmod 3: ds = " + ds.to_s if verbose
|
1582
|
+
s += ds
|
1583
|
+
|
1584
|
+
x = b
|
1585
|
+
y = -a
|
1586
|
+
p, q = x.divmod(y)
|
1587
|
+
l = x
|
1588
|
+
r = y * p + q
|
1589
|
+
ds = r.dis(l)
|
1590
|
+
puts "divmod 4: ds = " + ds.to_s if verbose
|
1591
|
+
s += ds
|
1592
|
+
|
1593
|
+
x, y = a.frexp
|
1594
|
+
l = a
|
1595
|
+
r = x.ldexp(y)
|
1596
|
+
ds = r.dis(l)
|
1597
|
+
puts "frexp and ldexp: ds = " + ds.to_s if verbose
|
1598
|
+
s += ds
|
1599
|
+
|
1600
|
+
x1 = R.c 1100000
|
1601
|
+
x2 = R.c "1100000 with comment which will be ignored"
|
1602
|
+
x3 = R.c(1200000.12)
|
1603
|
+
x4 = R.c("1200000.12")
|
1604
|
+
x5 = R.c("34567.89001953125e2")
|
1605
|
+
x6 = R.c("345.6789001953125E4")
|
1606
|
+
|
1607
|
+
l = x1
|
1608
|
+
r = x2
|
1609
|
+
ds = r.dis(l)
|
1610
|
+
puts "input 1: ds = " + ds.to_s if verbose
|
1611
|
+
s += ds
|
1612
|
+
|
1613
|
+
l = x3
|
1614
|
+
r = x4
|
1615
|
+
ds = r.dis(l)
|
1616
|
+
puts "input 2: ds = " + ds.to_s if verbose
|
1617
|
+
s += ds
|
1618
|
+
|
1619
|
+
l = x5
|
1620
|
+
r = x6
|
1621
|
+
ds = r.dis(l)
|
1622
|
+
puts "input 3: ds = " + ds.to_s if verbose
|
1623
|
+
s += ds
|
1624
|
+
|
1625
|
+
x = R.c9
|
1626
|
+
h = R.c(1e-6)
|
1627
|
+
fp = (x + h).erf
|
1628
|
+
fm = (x - h).erf
|
1629
|
+
l = (fp - fm)/(h * 2)
|
1630
|
+
r = (-x*x).exp*R.c2/R.pi.sqrt
|
1631
|
+
ds = r.dis(l)
|
1632
|
+
puts "erf derivative : ds = " + ds.to_s if verbose
|
1633
|
+
s += ds
|
1634
|
+
|
1635
|
+
t2 = Time.now
|
1636
|
+
puts "class of s is " + s.class.to_s + " ."
|
1637
|
+
puts "The error sum s is " + s.to_s + " ."
|
1638
|
+
puts "It should be close to 0."
|
1639
|
+
puts "Computation time was #{t2-t1} seconds."
|
1640
|
+
s
|
1641
|
+
end
|
1642
|
+
|
1643
|
+
protected :coerce, :asin_aux, :atan_aux, :exp10,
|
1644
|
+
:erfc_asy, :erf_ps
|
1645
|
+
|
1646
|
+
end # class R
|
1647
|
+
|
1648
|
+
end # module AppMath
|