long-decimal 0.01.03 → 0.02.01
Sign up to get free protection for your applications and to get access to all the features.
- data/README +130 -74
- data/Rakefile +4 -2
- data/VERSION +1 -1
- data/install.rb +6 -0
- data/lib/long-decimal-extra.rb +241 -44
- data/lib/long-decimal.rb +231 -335
- data/make_doc.rb +6 -0
- data/test/testlongdecimal-extra.rb +20 -7
- data/test/testlongdecimal.rb +309 -11
- data/test/testlongdeclib.rb +91 -22
- data/test/testrandlib.rb +7 -4
- data/test/testrandom-extra.rb +6 -4
- data/test/testrandom.rb +8 -4
- data/test/testrandpower.rb +6 -8
- data/version.rb +4 -3
- metadata +53 -46
data/README
CHANGED
@@ -1,32 +1,25 @@
|
|
1
1
|
Version
|
2
2
|
-------
|
3
3
|
|
4
|
-
This version ($Name:
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
long-decimal from becoming beta, so its solution will be postponed and
|
22
|
-
it will be resolved temporarily by moving it to a different library.
|
23
|
-
Ideas how to solve these issues are there, so eventually this
|
24
|
-
power-functionality will be working even for these cases.
|
25
|
-
|
26
|
-
All other functionality did not show any bugs during intensive
|
4
|
+
This version ($Name: BETA_02_01 $) is similar to the previous alpha-0.01.03.
|
5
|
+
|
6
|
+
Improvements over the previous version:
|
7
|
+
|
8
|
+
- full support for JRuby
|
9
|
+
- workaround for a bug in JRuby's Fixnum-multiplication, which has
|
10
|
+
been fixed for the next JRuby-version, but not for the existing installed base.
|
11
|
+
- calculation of powers with exponents having huge magnitude and with
|
12
|
+
bases close to 1 works better now.
|
13
|
+
- new method initialize_copy needed for clone()
|
14
|
+
- new method cbrt (cubic root)
|
15
|
+
- improved to_f which works better near Float::MAX
|
16
|
+
- new Method LongMath.log_f to obtain a Float-Approximation of the log
|
17
|
+
of a LongDecimal
|
18
|
+
- more and improved runit-Tests
|
19
|
+
|
20
|
+
All this functionality did not show any bugs during intensive
|
27
21
|
testing, so it could be assumed that the whole library is good for
|
28
|
-
beta and
|
29
|
-
mentioned above, which are being separated into another library.
|
22
|
+
beta and possibly even quite soon for production/stable.
|
30
23
|
|
31
24
|
This software development effort is hosted on RubyForge (
|
32
25
|
http://rubyforge.org/ ) under the project name "long-decimal", to be
|
@@ -34,15 +27,15 @@ found directly with http://rubyforge.org/projects/long-decimal/ . So
|
|
34
27
|
you should feel encouraged to look if there is a newer version, when
|
35
28
|
you install long-decimal.
|
36
29
|
|
37
|
-
This version is an
|
38
|
-
|
39
|
-
and
|
40
|
-
should be fine. log, log2, log10 and exp have been
|
41
|
-
for
|
42
|
-
cover all of these correctly. But it is still possible, that
|
43
|
-
cases the result deviates in the last digit by 1 or 2 from the
|
44
|
-
result. A deviation of slightly more than half of the unit of
|
45
|
-
last digit is already present. Improving on this would require an
|
30
|
+
This version is an beta-version.
|
31
|
+
Operations +, -, *, / and sqrt should be working properly
|
32
|
+
and could probably justify calling this release production/stable.
|
33
|
+
Even log and exp should be fine. log, log2, log10 and exp have been
|
34
|
+
tested for several million random values and the current version
|
35
|
+
should cover all of these correctly. But it is still possible, that
|
36
|
+
in some cases the result deviates in the last digit by 1 or 2 from the
|
37
|
+
required result. A deviation of slightly more than half of the unit of
|
38
|
+
the last digit is already present. Improving on this would require an
|
46
39
|
extensive extension of internal functionality to provide rounding
|
47
40
|
information in case of last digits being 50000..., where additional
|
48
41
|
digits would reveal if this really needs to be rounded up or down.
|
@@ -52,8 +45,28 @@ Speed could be improved as well.
|
|
52
45
|
|
53
46
|
It would be a good idea to do some more mathematical analysis on how
|
54
47
|
many digits are needed internally to guarantee the correctness of the
|
55
|
-
digits that are provided. But this will not be considered
|
56
|
-
requirement for the
|
48
|
+
digits that are provided. But this will not be considered a
|
49
|
+
requirement for the next versions to come.
|
50
|
+
|
51
|
+
A unique feature of long-decimal is the method
|
52
|
+
round_to_allowed_remainders(). This can be used for financial
|
53
|
+
calculations, where the last digit is required to be 0 or 5, which can
|
54
|
+
be achieved by calling
|
55
|
+
l.round_to_allowed_remainders([0, 5], 10, LongDecimalRoundingMode::ROUND_HALF_UP)
|
56
|
+
But this has been defined and developed in a more generic way, so the
|
57
|
+
modulus (here 10) and the set of allowed digits can be chosen
|
58
|
+
abritrarily, in which case the integral number, that is obtained by
|
59
|
+
disregarding the decimal point, is rounded in such a way that its
|
60
|
+
remainder modulo the modulus is in the list of allowed remainders,
|
61
|
+
after which the decimal point is inserted again.
|
62
|
+
|
63
|
+
Usage
|
64
|
+
-----
|
65
|
+
|
66
|
+
require 'rubygems'
|
67
|
+
require 'long-decimal'
|
68
|
+
|
69
|
+
(then use it.)
|
57
70
|
|
58
71
|
Test
|
59
72
|
----
|
@@ -67,30 +80,72 @@ correctly. The set of tests that is available now is considered to be
|
|
67
80
|
complete. As a policy a release is not created unless all tests
|
68
81
|
succeed. Running all tests can take a few minutes or even hours,
|
69
82
|
depending on your machine. Whatever is gained by making the software
|
70
|
-
run faster is used up again by adding more tests.
|
71
|
-
|
83
|
+
run faster is used up again by adding more tests. The regular tests are run by
|
84
|
+
|
85
|
+
ruby test/testlongdecimal.rb
|
72
86
|
|
73
|
-
|
87
|
+
This is the result of the test:
|
74
88
|
|
75
|
-
|
89
|
+
Finished in 5174.187655 seconds.
|
90
|
+
134 tests, 9461909 assertions, 0 failures, 0 errors
|
76
91
|
|
77
92
|
In addition random tests for exp, exp2, exp10, sqrt, log, log2 and
|
78
|
-
log10 can be run for a long time, using
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
93
|
+
log10 can be run for a long time, using
|
94
|
+
|
95
|
+
ruby test/testrandom.rb.
|
96
|
+
|
97
|
+
The functionality in lib/long-decimal-extra.rb, which contains some
|
98
|
+
more advanced and less tested methods, has its tests in
|
99
|
+
|
100
|
+
ruby test/testlongdecimal-extra.rb
|
101
|
+
|
102
|
+
and its random tests in
|
103
|
+
|
104
|
+
ruby test/testrandom-extra.rb
|
105
|
+
|
106
|
+
This is the result of the test:
|
107
|
+
|
108
|
+
Finished in 4115.851979 seconds.
|
109
|
+
12 tests, 976 assertions, 0 failures, 0 errors
|
110
|
+
|
111
|
+
Likewise tests for powers x to the yth with random x and y (which also
|
112
|
+
resided in long-decimal-extra.rb) can be tested for a long time using
|
113
|
+
|
114
|
+
ruby test/testrandpower.rb
|
115
|
+
|
116
|
+
These random tests require installation of the ruby-library
|
117
|
+
crypt-isaac for its random numbers, which works well with Linux or
|
118
|
+
Windows in combination with Cygwin. Installation of crypt-isaac is
|
119
|
+
slightly special. You have to download the gem, then install it using
|
120
|
+
gem install:
|
121
|
+
|
122
|
+
$ cd /tmp
|
123
|
+
$ wget http://rubyforge.org/frs/download.php/6461/crypt-isaac_0.9.1.gem
|
124
|
+
$ su -l
|
125
|
+
# cd /tmp
|
126
|
+
# gem install ruby-isaac
|
127
|
+
(you may need to adjust the URL for downloading the gem)
|
128
|
+
|
129
|
+
.... and then go into the directory, where the gem is
|
130
|
+
(/usr/local/lib/ruby/gems/1.8/gems/Crypt::ISAAC-0.9.1/ or
|
131
|
+
/usr/lib/ruby/gems/1.8/gems/Crypt::ISAAC-0.9.1/ or ...)
|
132
|
+
|
133
|
+
# cd /usr/local/lib/ruby/gems/1.8/gems/Crypt::ISAAC-0.9.1/
|
134
|
+
# ruby setup.rb install
|
135
|
+
# exit
|
136
|
+
$
|
137
|
+
|
138
|
+
If you actually want to run tests for long-decimal or
|
139
|
+
long-decimal-extra and find an error with it, please report it with
|
140
|
+
the lines of output revealing the bug on
|
86
141
|
http://rubyforge.org/projects/long-decimal/ -> tracker
|
87
142
|
|
88
143
|
Install
|
89
144
|
-------
|
90
145
|
|
91
146
|
(REMARK: installation has only been successfully tested on Linux with
|
92
|
-
ruby 1.8.
|
93
|
-
|
147
|
+
ruby 1.8.7 and on Windows 2000 with Cygwin and on Windows 2000 with
|
148
|
+
native ruby)
|
94
149
|
|
95
150
|
1. Using ruby-gems (preferred)
|
96
151
|
- open a shell window
|
@@ -121,7 +176,7 @@ ruby 1.8.4 and on Windows XP with Cygwin and ruby 1.8.4 and on Windows
|
|
121
176
|
$LONG_DECIMAL_VERSION is the version of long-decimal that you
|
122
177
|
have installed, like 0.00.20
|
123
178
|
on my machine that would be
|
124
|
-
/usr/local/lib/ruby/gems/1.8/doc/long-decimal-0.
|
179
|
+
/usr/local/lib/ruby/gems/1.8/doc/long-decimal-0.02.01/rdoc/index.html
|
125
180
|
|
126
181
|
2. Installing from the sources (it is preferred to use the
|
127
182
|
gem-installation, but since long-decimal is open-source-software you
|
@@ -135,7 +190,7 @@ ruby 1.8.4 and on Windows XP with Cygwin and ruby 1.8.4 and on Windows
|
|
135
190
|
- open a shell window
|
136
191
|
cd to the directory where you have downloaded the .tar.gz-file
|
137
192
|
unpack the file using tar
|
138
|
-
tar xfzvv long-decimal-
|
193
|
+
tar xfzvv long-decimal-beta-1_00.tar.gz
|
139
194
|
cd long-decimal
|
140
195
|
- now you can use rake for several operations
|
141
196
|
- rake test
|
@@ -154,61 +209,62 @@ in the gem-file. It is not provided as a separate file any more.
|
|
154
209
|
Bugs
|
155
210
|
----
|
156
211
|
|
157
|
-
Calculations of the kind
|
158
|
-
0.99999999999 ** 12423153125316415423512345234
|
159
|
-
that are performed using LongMath.power tend to take forever, if the
|
160
|
-
exponent is really big.
|
161
|
-
|
162
|
-
Method round_to_allowed_remainders() of LongDecimal has not been
|
163
|
-
tested enough.
|
164
|
-
|
165
212
|
It is considered somewhat arbitrary to disallow calculation
|
166
213
|
exponential functions if the result could not be expressed as Float.
|
167
214
|
This limitation should be removed, even though it has to be added,
|
168
|
-
that results of exponentiation that go beyond Float can only be
|
169
|
-
|
170
|
-
digits.
|
215
|
+
that results of exponentiation that go beyond Float can only be
|
216
|
+
handled with quite significant calculation effort, because they really
|
217
|
+
need more than 300 digits.
|
171
218
|
|
172
219
|
Certain calculations are too slow. Algorithms need to be optimized
|
173
220
|
for speed. The goal is to keep the algorithms in Ruby-code as long as
|
174
221
|
possible to make it easier to optimize the algorithm. If optimization
|
175
222
|
beyond this level will be needed, C-code might be used, preferably
|
176
|
-
based on an existing library.
|
223
|
+
based on an existing library. Since long-decimal is intending to
|
224
|
+
provide full support for JRuby as well, equivalent implementations in
|
225
|
+
Ruby or Java must be included for libraries written in C.
|
177
226
|
|
178
227
|
Even though some mathematical background has already been invested,
|
179
|
-
more effort from the theoretical side
|
180
|
-
internal precision parameters in such a way that correctness of
|
181
|
-
result to all given digits can be guaranteed with a minimum of
|
228
|
+
more effort from the theoretical side could be useful in order to
|
229
|
+
choose internal precision parameters in such a way that correctness of
|
230
|
+
the result to all given digits can be guaranteed with a minimum of
|
182
231
|
overhead. Currently parameters are probably slightly too careful,
|
183
232
|
which slows calculations down. But it is also possible that they are
|
184
|
-
insufficient for certain calculations, yielding wrong results
|
233
|
+
insufficient for certain calculations, yielding slightly wrong results
|
234
|
+
in some very rare situations, "wrong" meaning a deviation of a low
|
235
|
+
multiple of the unit.
|
185
236
|
|
186
237
|
rdoc-documentation and in-code comments are somewhat complete, but for
|
187
238
|
a sophisticated library like this additional external documentation
|
188
|
-
should be provided. Currently this does not exist at
|
239
|
+
should be provided in the long term. Currently this does not exist at
|
240
|
+
all.
|
189
241
|
|
190
242
|
Please report any bugs you come across on
|
191
243
|
http://rubyforge.org/projects/long-decimal -> Tracker.
|
192
244
|
|
193
|
-
The status of long-decimal is considered to be
|
245
|
+
The status of long-decimal is considered to be beta.
|
194
246
|
|
195
247
|
License
|
196
248
|
-------
|
197
249
|
|
198
250
|
Ruby's license or LGPL
|
199
251
|
Find copies of these licenses on http://www.gnu.org/ or http://www.ruby-lang.org/
|
252
|
+
� Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
|
200
253
|
|
201
254
|
Warranty
|
202
255
|
--------
|
203
256
|
|
204
|
-
This is a
|
205
|
-
|
206
|
-
|
207
|
-
|
257
|
+
This is a beta-version. Tests indicate that most functions work
|
258
|
+
relyably in all situations and all functions work relyably in most
|
259
|
+
situations. But do not expect too much! This is work in progress! I
|
260
|
+
do not take any responsibility. Please use it as it is, change it
|
261
|
+
according to the terms of the license or wait for a more stable
|
262
|
+
version (for which I can't take any warranty either...)
|
208
263
|
|
209
264
|
Author
|
210
265
|
------
|
211
266
|
|
212
267
|
Karl Brodowsky
|
268
|
+
IT Sky Consulting GmbH
|
213
269
|
http://www.velofahren.de/cgi-bin/mailform.cgi
|
214
270
|
(no direct mail address because I do not like spam)
|
data/Rakefile
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
#
|
2
2
|
# Rakefile for long-decimal project
|
3
3
|
#
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
|
5
|
+
#
|
6
|
+
# CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/Rakefile,v 1.4 2009/04/15 19:29:37 bk1 Exp $
|
7
|
+
# CVS-Label: $Name: BETA_02_01 $
|
6
8
|
# Author: $Author: bk1 $ (Karl Brodowsky)
|
7
9
|
#
|
8
10
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.01
|
1
|
+
0.02.01
|
data/install.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
#
|
4
|
+
# CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/install.rb,v 1.3 2009/04/15 19:29:37 bk1 Exp $
|
5
|
+
# CVS-Label: $Name: BETA_02_01 $
|
6
|
+
# Author: $Author: bk1 $ (Karl Brodowsky)
|
7
|
+
#
|
8
|
+
|
3
9
|
require 'rbconfig'
|
4
10
|
require 'fileutils'
|
5
11
|
include FileUtils::Verbose
|
data/lib/long-decimal-extra.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
#
|
2
2
|
# long-decimal-extra.rb -- Arbitrary precision decimals with fixed decimal point
|
3
3
|
#
|
4
|
-
#
|
5
|
-
#
|
4
|
+
# (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
|
5
|
+
#
|
6
|
+
# CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/long-decimal-extra.rb,v 1.9 2009/04/21 16:56:49 bk1 Exp $
|
7
|
+
# CVS-Label: $Name: BETA_02_01 $
|
6
8
|
# Author: $Author: bk1 $ (Karl Brodowsky)
|
7
9
|
#
|
8
10
|
require "complex"
|
@@ -13,6 +15,106 @@ require "bigdecimal"
|
|
13
15
|
|
14
16
|
# require "bigdecimal/math"
|
15
17
|
|
18
|
+
class Rational
|
19
|
+
alias :to_g :to_f
|
20
|
+
|
21
|
+
FLOAT_MAX_I = Float::MAX.to_i
|
22
|
+
|
23
|
+
def to_f
|
24
|
+
numerator = @numerator
|
25
|
+
denominator = @denominator
|
26
|
+
sign = numerator <=> 0
|
27
|
+
if (sign.zero?)
|
28
|
+
return 0.0
|
29
|
+
elsif sign < 0
|
30
|
+
numerator = -numerator
|
31
|
+
end
|
32
|
+
while numerator >= FLOAT_MAX_I || denominator >= FLOAT_MAX_I do
|
33
|
+
numerator >>= 8
|
34
|
+
denominator >>= 8
|
35
|
+
if (denominator == 0)
|
36
|
+
raise ZeroDivisionError, "denominator too close to zero: #{@numerator}/{@denominator}"
|
37
|
+
elsif numerator == 0
|
38
|
+
return 0.0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
return numerator.to_f / denominator.to_f
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class LongDecimal
|
46
|
+
|
47
|
+
#
|
48
|
+
# convert self into Float
|
49
|
+
# this works straitforward by dividing numerator by power of 10 in
|
50
|
+
# float-arithmetic, in all cases where numerator and denominator are
|
51
|
+
# within the ranges expressable as Floats. Goes via string
|
52
|
+
# representation otherwise.
|
53
|
+
#
|
54
|
+
def to_g
|
55
|
+
# handle overflow: raise exception
|
56
|
+
if (self.abs > LongMath::MAX_FLOATABLE) then
|
57
|
+
raise ArgumentError, "self=#{self.inspect} cannot be expressed as Float"
|
58
|
+
end
|
59
|
+
|
60
|
+
# handle underflow: return 0.0
|
61
|
+
if (self.abs < LongMath::MIN_FLOATABLE) then
|
62
|
+
puts "-> 0.0"
|
63
|
+
return 0.0
|
64
|
+
end
|
65
|
+
|
66
|
+
if (self < 0) then
|
67
|
+
puts "-> negate"
|
68
|
+
return -(-self).to_g
|
69
|
+
end
|
70
|
+
|
71
|
+
dividend = numerator
|
72
|
+
divisor = denominator
|
73
|
+
|
74
|
+
if (divisor == 1) then
|
75
|
+
puts "-> /1"
|
76
|
+
return dividend.to_f
|
77
|
+
elsif dividend.abs <= LongMath::MAX_FLOATABLE then
|
78
|
+
puts "-> dividend <= MAX_FLOATABLE"
|
79
|
+
if (divisor.abs > LongMath::MAX_FLOATABLE) then
|
80
|
+
puts "-> divisor > MAX_FLOATABLE"
|
81
|
+
qe = scale - Float::MAX_10_EXP
|
82
|
+
q = 10**qe
|
83
|
+
puts "-> q=#{q}"
|
84
|
+
f = (dividend / q).to_f
|
85
|
+
puts "-> f=#{f}"
|
86
|
+
d = LongMath::MAX_FLOATABLE10
|
87
|
+
puts "-> d=#{d}"
|
88
|
+
y = f / d
|
89
|
+
puts "-> y=#{y}"
|
90
|
+
return y
|
91
|
+
else
|
92
|
+
puts "-> divisor <= MAX_FLOATABLE"
|
93
|
+
f = dividend.to_f
|
94
|
+
return f / divisor
|
95
|
+
end
|
96
|
+
elsif dividend.abs < divisor
|
97
|
+
puts "-> < 1"
|
98
|
+
# self is between -1 and 1
|
99
|
+
|
100
|
+
# factor = dividend.abs.div(LongMath::MAX_FLOATABLE)
|
101
|
+
# digits = factor.to_ld.int_digits10
|
102
|
+
# return LongDecimal(dividend.div(10**digits), scale -digits).to_f
|
103
|
+
return self.to_s.to_f
|
104
|
+
else
|
105
|
+
puts "-> >= 1"
|
106
|
+
q = dividend.abs / divisor
|
107
|
+
if (q.abs > 1000000000000000000000)
|
108
|
+
puts "-> > 1000000000000000000000"
|
109
|
+
return q.to_f
|
110
|
+
else
|
111
|
+
puts "-> <= 1000000000000000000000"
|
112
|
+
return self.to_s.to_f
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
16
118
|
#
|
17
119
|
# LongMath provides some helper functions to support LongDecimal and
|
18
120
|
# LongDecimalQuot, mostly operating on integers. They are used
|
@@ -91,29 +193,50 @@ module LongMath
|
|
91
193
|
return y
|
92
194
|
end
|
93
195
|
|
196
|
+
|
197
|
+
#
|
198
|
+
# calculate the natural logarithm of x as floating point number,
|
199
|
+
# even if x cannot reasonably be expressed as Float.
|
200
|
+
#
|
201
|
+
def LongMath.log_f(x)
|
202
|
+
raise TypeError, "x=#{x.inspect} must not be positive" unless x > 0
|
203
|
+
unless x.kind_of? LongDecimal
|
204
|
+
x = x.to_ld(18, LongDecimalRoundingMode::ROUND_HALF_UP)
|
205
|
+
end
|
206
|
+
y = 0
|
207
|
+
while (x > LongMath::MAX_FLOATABLE)
|
208
|
+
y += LOG_1E100
|
209
|
+
x = x.move_point_left(100)
|
210
|
+
end
|
211
|
+
while (x < LongMath::MIN_FLOATABLE)
|
212
|
+
y -= LOG_1E100
|
213
|
+
x = x.move_point_right(100)
|
214
|
+
end
|
215
|
+
x_f = x.to_f
|
216
|
+
y += Math.log(x_f)
|
217
|
+
y
|
218
|
+
end
|
219
|
+
|
94
220
|
private
|
95
221
|
|
222
|
+
LOG_1E100 = Math.log(1e100)
|
223
|
+
|
96
224
|
#
|
97
225
|
# internal helper method for calculating the internal precision for power
|
98
226
|
#
|
99
227
|
def LongMath.calc_iprec_for_power(x, y, prec)
|
100
228
|
|
101
|
-
logx_f =
|
102
|
-
if (x.abs <= LongMath::MAX_FLOATABLE)
|
103
|
-
x_f = x.to_f
|
104
|
-
logx_f = Math.log(x_f.abs)
|
105
|
-
else
|
106
|
-
logx_f = LongMath.log(x, 15, LongMath::ROUND_UP)
|
107
|
-
end
|
229
|
+
logx_f = LongMath.log_f(x.abs)
|
108
230
|
|
109
231
|
y_f = nil
|
110
232
|
if (y.abs <= LongMath::MAX_FLOATABLE) then
|
111
233
|
y_f = y.to_f
|
112
234
|
else
|
113
|
-
y_f = y.round_to_scale(
|
235
|
+
y_f = y.round_to_scale(18, LongMath::ROUND_UP)
|
114
236
|
end
|
115
237
|
|
116
238
|
logx_y_f = logx_f * y_f
|
239
|
+
|
117
240
|
if (logx_y_f.abs > LongMath::MAX_FLOATABLE) then
|
118
241
|
raise ArgumentError, "power would be way too big: y*log(x)=#{logx_y_f}";
|
119
242
|
end
|
@@ -126,21 +249,9 @@ module LongMath
|
|
126
249
|
iprec_x -= (logx_f/LOG10).round
|
127
250
|
end
|
128
251
|
if (y_f.abs < 1)
|
129
|
-
logy_f =
|
130
|
-
if (y_f.kind_of? Float) then
|
131
|
-
logy_f = Math.log(y_f.abs)
|
132
|
-
else
|
133
|
-
logy_f = LongMath.log(y_f.abs, 15, LongMath::ROUND_UP)
|
134
|
-
if (logy_f.abs > LongMath::MAX_FLOATABLE) then
|
135
|
-
raise ArgumentError, "exponent would be way too big: y=#{y} logy_f=#{logy_f}";
|
136
|
-
end
|
137
|
-
logy_f = logy_f.to_f
|
138
|
-
end
|
139
|
-
# puts("x=#{x} y=#{y} x_f=#{x_f} y_f=#{y_f} logx_f=#{logx_f} logy_f=#{logy_f} logx_y_f=#{logx_y_f}\n")
|
252
|
+
logy_f = LongMath.log_f(y.abs)
|
140
253
|
iprec_y -= (logy_f/LOG10).round
|
141
254
|
end
|
142
|
-
# puts("x=#{x} y=#{y} x_f=#{x_f} y_f=#{y_f} logx_f=#{logx_f} logy_f=#{logy_f} logx_y_f=#{logx_y_f}\n")
|
143
|
-
# puts("\niprec: x=#{x} y=#{y} iprec=#{iprec} iprec_x=#{iprec_x} iprec_y=#{iprec_y}\n")
|
144
255
|
[ iprec, iprec_x, iprec_y, logx_y_f ]
|
145
256
|
|
146
257
|
end
|
@@ -149,8 +260,8 @@ module LongMath
|
|
149
260
|
|
150
261
|
#
|
151
262
|
# calc the power of x with exponent y to the given precision as
|
152
|
-
# LongDecimal. Only supports values of y such that
|
153
|
-
# fits into a float
|
263
|
+
# LongDecimal. Only supports values of y such that the result still
|
264
|
+
# fits into a float
|
154
265
|
#
|
155
266
|
def LongMath.power(x, y, prec, mode = LongMath.standard_mode)
|
156
267
|
|
@@ -158,8 +269,7 @@ module LongMath
|
|
158
269
|
raise TypeError, "y=#{y} must be numeric" unless y.kind_of? Numeric
|
159
270
|
raise TypeError, "x=#{x.inspect} must not be greater #{MAX_FLOATABLE}" unless x.abs <= MAX_FLOATABLE
|
160
271
|
raise TypeError, "y=#{y.inspect} must not be greater #{MAX_FLOATABLE}" unless y.abs <= MAX_FLOATABLE
|
161
|
-
|
162
|
-
# raise TypeError, "x=#{x.inspect} must not negative" unless x >= 0 || (y.kind_of? Integer) || (y.kind_of? LongDecimalBase) && y.is_int?
|
272
|
+
raise TypeError, "y=#{y.inspect} must not be negative if base is zero}" if y < 0 && x.zero?
|
163
273
|
raise TypeError, "x=#{x.inspect} must not negative" unless x >= 0
|
164
274
|
check_is_prec(prec, "prec")
|
165
275
|
check_is_mode(mode, "mode")
|
@@ -175,17 +285,18 @@ module LongMath
|
|
175
285
|
return LongDecimal.one!(prec)
|
176
286
|
end
|
177
287
|
|
178
|
-
# els
|
179
288
|
# could be result with our precision
|
180
289
|
# x ** y <= 10**-s/2 <=> y * log(x) <= -s log(10) - log(2)
|
181
290
|
|
182
291
|
iprec, iprec_x, iprec_y, logx_y_f = calc_iprec_for_power(x, y, prec)
|
292
|
+
# puts "x=#{x} y=#{y} prec=#{prec} iprec=#{iprec} iprec_x=#{iprec_x} iprec_y=#{iprec_y} logx_y_f=#{logx_y_f}: checking x < 1 && y > 0 || x > 1 && y < 0=#{x < 1 && y > 0 || x > 1 && y < 0}"
|
293
|
+
$stdout.flush
|
183
294
|
if (x < 1 && y > 0 || x > 1 && y < 0) then
|
295
|
+
# puts "checking if zero logx_y_f=#{logx_y_f} <= #{- prec * LOG10 - LOG2}"
|
184
296
|
if (logx_y_f <= - prec * LOG10 - LOG2) then
|
185
297
|
return LongDecimal.zero!(prec)
|
186
298
|
end
|
187
299
|
end
|
188
|
-
# puts("x=#{x} y=#{y} iprec=#{iprec} iprec_x=#{iprec_x} iprec_y=#{iprec_y} prec=#{prec}")
|
189
300
|
|
190
301
|
unless (x.kind_of? LongDecimalBase) || (x.kind_of? Integer)
|
191
302
|
x = x.to_ld(iprec_x, mode)
|
@@ -202,7 +313,6 @@ module LongMath
|
|
202
313
|
y2 = y*2
|
203
314
|
if (y2.kind_of? LongDecimalBase) && y2.is_int? then
|
204
315
|
y2 = y2.to_i
|
205
|
-
puts("y2=#{y2}")
|
206
316
|
end
|
207
317
|
if (y2.kind_of? Integer)
|
208
318
|
x = LongMath.sqrt(x, 2*iprec_x, mode)
|
@@ -211,19 +321,15 @@ module LongMath
|
|
211
321
|
end
|
212
322
|
if (y.kind_of? Integer)
|
213
323
|
unless x.kind_of? LongDecimal
|
214
|
-
# x = x.to_ld(prec)
|
215
324
|
x = x.to_ld(iprec_x)
|
216
325
|
end
|
217
|
-
# z = x ** y
|
218
326
|
z = LongMath.ipower(x, y, 2*iprec, mode)
|
219
|
-
# puts("x=#{x} y=#{y} z=#{z} y int")
|
220
327
|
return z.to_ld(prec, mode)
|
221
328
|
end
|
222
329
|
|
223
330
|
# it can be assumed that the exponent is not an integer, so it should
|
224
331
|
# be converted into LongDecimal
|
225
332
|
unless (y.kind_of? LongDecimal)
|
226
|
-
# y = y.to_ld(prec, mode)
|
227
333
|
y = y.to_ld(iprec_y, mode)
|
228
334
|
end
|
229
335
|
|
@@ -235,14 +341,33 @@ module LongMath
|
|
235
341
|
# if necessary to LongDecimalBase
|
236
342
|
y = -y
|
237
343
|
x = (1/x).round_to_scale(iprec_x*2, mode)
|
238
|
-
iprec, iprec_x, iprec_y = calc_iprec_for_power(x, y, prec)
|
344
|
+
iprec, iprec_x, iprec_y, logx_y_f = calc_iprec_for_power(x, y, prec)
|
345
|
+
# puts "x=#{x} y=#{y} prec=#{prec} iprec=#{iprec} iprec_x=#{iprec_x} iprec_y=#{iprec_y} logx_y_f=#{logx_y_f}: checking x < 1 && y > 0 || x > 1 && y < 0=#{x < 1 && y > 0 || x > 1 && y < 0}"
|
346
|
+
$stdout.flush
|
347
|
+
if (x < 1 && y > 0 || x > 1 && y < 0) then
|
348
|
+
# puts "checking if zero logx_y_f=#{logx_y_f} <= #{- prec * LOG10 - LOG2}"
|
349
|
+
if (logx_y_f <= - prec * LOG10 - LOG2) then
|
350
|
+
return LongDecimal.zero!(prec)
|
351
|
+
end
|
352
|
+
end
|
239
353
|
end
|
240
354
|
|
241
355
|
# exponent is split in two parts, an integer part and a
|
242
356
|
# LongDecimal with absolute value <= 0.5
|
243
357
|
y0 = y.round_to_scale(0, LongMath.standard_imode).to_i
|
244
|
-
|
245
|
-
|
358
|
+
x0 = x
|
359
|
+
point_shift = 0
|
360
|
+
while x0 > LongMath::MAX_FLOATABLE
|
361
|
+
x0 = x0.move_point_left(100)
|
362
|
+
point_shift += 100
|
363
|
+
end
|
364
|
+
z0 = LongMath.ipower(x0, y0, 2*(iprec + point_shift), mode)
|
365
|
+
if (point_shift > 0)
|
366
|
+
unless z0.kind_of? LongDecimal
|
367
|
+
z0 = z0.to_ld(2*(iprec + point_shift))
|
368
|
+
end
|
369
|
+
z0 = z0.move_point_right(point_shift * y0)
|
370
|
+
end
|
246
371
|
y1 = y - y0
|
247
372
|
prec_extra = 0
|
248
373
|
if (y0 > 0)
|
@@ -263,14 +388,84 @@ module LongMath
|
|
263
388
|
#
|
264
389
|
def LongMath.ipower(x, y, prec, mode)
|
265
390
|
|
266
|
-
|
267
|
-
raise TypeError, "
|
268
|
-
raise TypeError, "
|
269
|
-
raise TypeError, "
|
391
|
+
t0 = Time.now
|
392
|
+
raise TypeError, "base x=#{x} must be numeric" unless x.kind_of? Numeric
|
393
|
+
raise TypeError, "exponent y=#{y} must be integer" unless y.kind_of? Integer
|
394
|
+
raise TypeError, "base x=#{x.inspect} must not be greater MAX_FLOATABLE=#{MAX_FLOATABLE}" unless x.abs <= MAX_FLOATABLE
|
395
|
+
raise TypeError, "exponent y=#{y.inspect} must not be greater MAX_FLOATABLE=#{MAX_FLOATABLE}" unless y.abs <= MAX_FLOATABLE
|
270
396
|
check_is_prec(prec, "prec")
|
271
397
|
check_is_mode(mode, "mode")
|
272
398
|
|
273
|
-
|
399
|
+
if (y.zero?)
|
400
|
+
return 1
|
401
|
+
elsif ! (x.kind_of? LongDecimalBase) || x.scale * y.abs <= prec
|
402
|
+
# puts "x=#{x} y=#{y} using **"
|
403
|
+
return x ** y
|
404
|
+
elsif (y < 0)
|
405
|
+
l = Math.log10(x.abs.to_f)
|
406
|
+
if (l > 0)
|
407
|
+
prec += (2*l).ceil
|
408
|
+
end
|
409
|
+
# return (1/LongMath.ipower(x, -y, prec + 2, mode)).round_to_scale(prec, mode)
|
410
|
+
xi = 1/x
|
411
|
+
# puts "x=#{x} y=#{y} prec=#{prec} using (1/x)**y xi=#{xi}"
|
412
|
+
xr = xi.round_to_scale(prec + 6, mode)
|
413
|
+
return LongMath.ipower(xr, -y, prec, mode)
|
414
|
+
else
|
415
|
+
# y > 0
|
416
|
+
# puts "x=#{x} y=#{y} regular"
|
417
|
+
cnt = 0
|
418
|
+
z = x
|
419
|
+
y0 = y
|
420
|
+
x0 = x
|
421
|
+
while true do
|
422
|
+
|
423
|
+
cnt++
|
424
|
+
y -= 1
|
425
|
+
if (y.zero?)
|
426
|
+
break
|
427
|
+
end
|
428
|
+
while (y & 0x01) == 0 do
|
429
|
+
|
430
|
+
cnt++
|
431
|
+
y = y >> 1
|
432
|
+
x = (x*x)
|
433
|
+
if (x.kind_of? LongDecimalBase)
|
434
|
+
x = x.round_to_scale(prec+4, mode)
|
435
|
+
end
|
436
|
+
if (cnt > 1000)
|
437
|
+
puts("ipower x=#{x} y=#{y} cnt=#{cnt} z=#{z} t=#{Time.now - t0}")
|
438
|
+
cnt = 0
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|
442
|
+
z = (z*x)
|
443
|
+
if (z.kind_of? LongDecimalBase)
|
444
|
+
z = z.round_to_scale(prec+3, mode)
|
445
|
+
if (z.zero?)
|
446
|
+
break
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
z = z.round_to_scale(prec, mode)
|
451
|
+
return z
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
#
|
456
|
+
# internal functionality to calculate the y-th power of x assuming
|
457
|
+
# that y is an integer
|
458
|
+
# prec is a hint on how much internal precision is needed at most
|
459
|
+
# final rounding is left to the caller
|
460
|
+
#
|
461
|
+
def LongMath.ipower_with_measurement(x, y, prec, mode)
|
462
|
+
|
463
|
+
raise TypeError, "base x=#{x} must be numeric" unless x.kind_of? Numeric
|
464
|
+
raise TypeError, "exponent y=#{y} must be integer" unless y.kind_of? Integer
|
465
|
+
raise TypeError, "base x=#{x.inspect} must not be greater MAX_FLOATABLE=#{MAX_FLOATABLE}" unless x.abs <= MAX_FLOATABLE
|
466
|
+
raise TypeError, "exponent y=#{y.inspect} must not be greater MAX_FLOATABLE=#{MAX_FLOATABLE}" unless y.abs <= MAX_FLOATABLE
|
467
|
+
check_is_prec(prec, "prec")
|
468
|
+
check_is_mode(mode, "mode")
|
274
469
|
|
275
470
|
if (y.zero?)
|
276
471
|
return 1
|
@@ -284,8 +479,10 @@ module LongMath
|
|
284
479
|
return 1/LongMath.ipower(x, -y, prec, mode)
|
285
480
|
else
|
286
481
|
# y > 0
|
287
|
-
|
482
|
+
cnt = 0
|
288
483
|
z = x
|
484
|
+
y0 = y
|
485
|
+
x0 = x
|
289
486
|
while true do
|
290
487
|
|
291
488
|
cnt++
|
@@ -313,8 +510,8 @@ module LongMath
|
|
313
510
|
end
|
314
511
|
|
315
512
|
end
|
513
|
+
return z
|
316
514
|
end
|
317
|
-
return z
|
318
515
|
end
|
319
516
|
|
320
517
|
#
|