long-decimal 0.01.03 → 0.02.01
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/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
|
#
|