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 CHANGED
@@ -1,32 +1,25 @@
1
1
  Version
2
2
  -------
3
3
 
4
- This version ($Name: ALPHA_01_03 $) is functionally mostly identical with the
5
- previous alpha-0.01.02. Support for rounding to any given set of last
6
- digits, for example allowing only 0 and 5 as last digit, has been
7
- added and tests for this kind of functionality are considered to be
8
- complete. This functionality is useful for rounding patterns within
9
- currencies where the smallest unit is 0.05, not 0.01, like CHF. In
10
- this case, remainders 0 and 5 modulo 10 are required. For currencies
11
- where the smallest unit is 0.02, it would be 0, 2, 4, 8 modulo 10.
12
- But this has been implemented in a more general way, so it would also
13
- be possible to restrict the rounding results to any set modulo any
14
- integral number >= 2.
15
-
16
- Extensive testing has shown that apart from this new functionality
17
- there seems to be an issue when calculating powers of LongDecimal
18
- where the base is slightly less than 1 and the exponent is a big
19
- positive number. In some cases where this should result in 0, the
20
- calculations seem to take time forever. This issue should not stop
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 maybe even production/stable, apart from the issues
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 alpha-version, which might also be called
38
- "pre-beta". Operations +, -, *, / and sqrt should be working properly
39
- and would justify calling this release at least beta. Even log and exp
40
- should be fine. log, log2, log10 and exp have been tested
41
- for more than a million random values and the current version should
42
- cover all of these correctly. But it is still possible, that in some
43
- cases the result deviates in the last digit by 1 or 2 from the real
44
- result. A deviation of slightly more than half of the unit of the
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 as a
56
- requirement for the beta-version.
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. This is the result
71
- of the test:
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
- Finished in 1160.808707 seconds.
87
+ This is the result of the test:
74
88
 
75
- 131 tests, 45612 assertions, 0 failures, 0 errors
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 the script test/testrandom.rb.
79
- Likewise tests for powers x to the yth with random x and y can be
80
- tested for a long time using test/testrandpower.rb
81
-
82
- These two require installation of the ruby-library crypt-isaac for its
83
- random numbers, which works well with Linux or Windows and Cygwin. If
84
- you actually want to run this and find an error with it, please report
85
- it with the lines of output revealing the bug on
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.4 and on Windows XP with Cygwin and ruby 1.8.4 and on Windows
93
- 2000 with ruby 1.8.2)
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.00.20/rdoc/index.html
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-alpha-1_00.tar.gz
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 handled
169
- at a great cost of performance, because they really need more than 300
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 is needed in order to choose
180
- internal precision parameters in such a way that correctness of the
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 all.
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 alpha.
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 alpha-version. Do not expect too much! This is work in
205
- progress! I do not take any responsibility. Please use it as it is,
206
- change it according to the terms of the license or wait for a more
207
- stable version (for which I can't take any warranty either...)
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
- # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/Rakefile,v 1.3 2006/04/01 08:52:06 bk1 Exp $
5
- # CVS-Label: $Name: ALPHA_01_03 $
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.03
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
@@ -1,8 +1,10 @@
1
1
  #
2
2
  # long-decimal-extra.rb -- Arbitrary precision decimals with fixed decimal point
3
3
  #
4
- # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/long-decimal-extra.rb,v 1.1 2007/08/19 19:23:33 bk1 Exp $
5
- # CVS-Label: $Name: ALPHA_01_03 $
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 = nil
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(15, LongMath::ROUND_UP)
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 = nil
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 exp(y) still
153
- # fits into a float (y <= 709)
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
- # raise TypeError, "y=#{y.inspect} must not be greater #{MAX_EXP_ABLE}" unless y <= MAX_EXP_ABLE
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
- # z0 = x**y0
245
- z0 = LongMath.ipower(x, y0, 2*iprec, mode)
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
- raise TypeError, "x=#{x} must be numeric" unless x.kind_of? Numeric
267
- raise TypeError, "y=#{y} must be integer" unless y.kind_of? Integer
268
- raise TypeError, "x=#{x.inspect} must not be greater #{MAX_FLOATABLE}" unless x.abs <= MAX_FLOATABLE
269
- raise TypeError, "y=#{y.inspect} must not be greater #{MAX_FLOATABLE}" unless y.abs <= MAX_FLOATABLE
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
- cnt = 0
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
- # puts("ipower y>0 x=#{x} y=#{y} prec=#{prec}")
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
  #