long-decimal 0.00.14 → 0.00.15

Sign up to get free protection for your applications and to get access to all the features.
data/lib/long-decimal.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  #
2
2
  # long-decimal.rb -- Arbitrary precision decimals with fixed decimal point
3
3
  #
4
- # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/long-decimal.rb,v 1.2 2006/03/19 11:17:55 bk1 Exp $
5
- # CVS-Label: $Name: PRE_ALPHA_0_14 $
4
+ # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/long-decimal.rb,v 1.6 2006/03/20 21:38:32 bk1 Exp $
5
+ # CVS-Label: $Name: PRE_ALPHA_0_15 $
6
6
  # Author: $Author: bk1 $ (Karl Brodowsky)
7
7
  #
8
8
  require "complex"
@@ -53,1553 +53,1322 @@ module LongDecimalRoundingMode
53
53
  end # LongDecimalRoundingMode
54
54
 
55
55
  #
56
- # LongMath provides some helper functions to support LongDecimal and
57
- # LongDecimalQuot, mostly operating on integers. They are used
58
- # internally here, but possibly they can be used elsewhere as well.
59
- # In addition LongMath provides methods like those in Math, but for
60
- # LongDecimal instead of Float.
56
+ # class for holding fixed point long decimal numbers
57
+ # these can be considered as a pair of two integer. One contains the
58
+ # digits and the other one the position of the decimal point.
61
59
  #
62
- module LongMath
60
+ class LongDecimal < Numeric
61
+ @RCS_ID='-$Id: long-decimal.rb,v 1.6 2006/03/20 21:38:32 bk1 Exp $-'
63
62
 
64
63
  include LongDecimalRoundingMode
65
64
 
66
- MAX_FLOATABLE = Float::MAX.to_i
67
- MAX_EXP_ABLE = Math.log(MAX_FLOATABLE).to_i
68
- LOG2 = Math.log(2.0)
69
- LOG10 = Math.log(10.0)
65
+ # MINUS_ONE = LongDecimal(-1)
66
+ # ZERO = LongDecimal(0)
67
+ # ONE = LongDecimal(1)
68
+ # TWO = LongDecimal(2)
69
+ # TEN = LongDecimal(10)
70
70
 
71
71
  #
72
- # helper method for internal use: checks if word_len is a reasonable
73
- # size for splitting a number into parts
72
+ # initialization
73
+ # parameters:
74
+ # 1. LongDecimal.new!(x) where x is a string or a number
75
+ # the resulting LongDecimal holds the number x, possibly rounded
76
+ # 2. LongDecimal.new!(x, s) where x is a string or a number and s is the scale
77
+ # the resulting LongDecimal holds the number x / 10**s
74
78
  #
75
- def LongMath.check_word_len(word_len, name="word_len")
76
- raise TypeError, "#{name} must be a positive number <= 1024" unless (word_len.kind_of? Fixnum) && word_len > 0 && word_len <= 1024
77
- word_len
79
+ def LongDecimal.new!(x, s = 0)
80
+ new(x, s)
78
81
  end
79
82
 
80
83
  #
81
- # helper method for internal use: checks if parameter x is an Integer
84
+ # creates a LongDecimal representing zero with the given number of
85
+ # digits after the decimal point (scale=s)
82
86
  #
83
- def LongMath.check_is_int(x, name="x")
84
- raise TypeError, "#{name}=#{x.inspect} must be Integer" unless x.kind_of? Integer
87
+ def LongDecimal.zero!(s = 0)
88
+ new(0, s)
85
89
  end
86
90
 
87
- #
88
- # helper method for internal use: checks if parameter x is a LongDecimal
89
- #
90
- def LongMath.check_is_ld(x, name="x")
91
- raise TypeError, "x=#{x.inspect} must be LongDecimal" unless x.kind_of? LongDecimal
92
- end
93
91
 
94
92
  #
95
- # helper method for internal use: checks if parameter x is a
96
- # reasonable value for the precision (scale) of a LongDecimal
93
+ # creates a LongDecimal representing one with the given number of
94
+ # digits after the decimal point (scale=s)
97
95
  #
98
- def LongMath.check_is_prec(prec, name="prec")
99
- check_is_int(prec, "prec")
100
- raise TypeError, "#{name}=#{prec.inspect} must be >= 0" unless prec >= 0
96
+ def LongDecimal.one!(s = 0)
97
+ new(10**s, s)
101
98
  end
102
99
 
100
+
103
101
  #
104
- # helper method for internal use: checks if parameter x is a
105
- # rounding mode (instance of RoundingModeClass)
102
+ # creates a LongDecimal representing two with the given number of
103
+ # digits after the decimal point (scale=s)
106
104
  #
107
- def LongMath.check_is_mode(mode, name="mode")
108
- raise TypeError, "#{name}=#{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
105
+ def LongDecimal.two!(s = 0)
106
+ new(2*10**s, s)
109
107
  end
110
108
 
109
+
111
110
  #
112
- # split number (Integer) x into parts of word_len bits each such
113
- # that the concatenation of these parts as bit patterns is x
114
- # (the opposite of merge_from_words)
111
+ # creates a LongDecimal representing ten with the given number of
112
+ # digits after the decimal point (scale=s)
115
113
  #
116
- def LongMath.split_to_words(x, word_len = 32)
117
- check_word_len(word_len)
118
- check_is_int(x, "x")
119
- m = x.abs
120
- s = (x <=> 0)
121
- bit_pattern = (1 << word_len) - 1
122
- words = []
123
- while (m != 0 || words.length == 0) do
124
- w = m & bit_pattern
125
- m = m >> word_len
126
- words.unshift(w)
127
- end
128
- if (s < 0) then
129
- words[0] = -words[0]
130
- end
131
- words
114
+ def LongDecimal.ten!(s = 0)
115
+ new(10**(s+1), s)
132
116
  end
133
117
 
118
+
134
119
  #
135
- # concatenate numbers given in words as bit patterns
136
- # (the opposite of split_to_words)
120
+ # creates a LongDecimal representing minus one with the given number of
121
+ # digits after the decimal point (scale=s)
137
122
  #
138
- def LongMath.merge_from_words(words, word_len = 32)
139
- check_word_len(word_len)
140
- raise TypeError, "words must be array of length > 0" unless (words.kind_of? Array) && words.length > 0
141
- y = 0
142
- s = (words[0] <=> 0)
143
- if (s < 0) then
144
- words[0] = -words[0]
145
- end
146
- words.each do |w|
147
- y = y << word_len
148
- y += w
149
- end
150
- if (s < 0) then
151
- y = -y
152
- end
153
- y
123
+ def LongDecimal.minus_one!(s = 0)
124
+ new(-1*10**s, s)
154
125
  end
155
126
 
156
- #
157
127
 
158
- # calculate the square root of an integer x using bitwise algorithm
159
- # the result is rounded to an integer y such that
160
- # y**2�<=�x�<�(y+1)**2
161
128
  #
162
- def LongMath.sqrtb(x)
163
- a = sqrtb_with_remainder(x)
164
- a[0]
129
+ # creates a LongDecimal representing a power of ten with the given
130
+ # exponent e and with the given number of digits after the decimal
131
+ # point (scale=s)
132
+ #
133
+ def LongDecimal.power_of_ten!(e, s = 0)
134
+ LongMath.check_is_int(e, "e")
135
+ raise TypeError, "negative 1st arg \"#{e.inspect}\"" if e < 0
136
+ new(10**(s+e), s)
165
137
  end
166
138
 
139
+
167
140
  #
168
- #�calculate�the�an�integer�s�>=�0�and�a�remainder�r�>=�0�such�that
169
- #�x�=�s**2�+�r�and�s**2�<=�x�<�(s+1)**2
170
- # the bitwise algorithm is used, which works well for relatively
171
- # small values of x.
141
+ # initialization
142
+ # parameters:
143
+ # LongDecimal.new(x, s) where x is a string or a number and s is the scale
144
+ # the resulting LongDecimal holds the number x / 10**s
172
145
  #
173
- def LongMath.sqrtb_with_remainder(x)
174
- check_is_int(x, "x")
146
+ def initialize(x, s)
175
147
 
176
- s = (x <=> 0)
177
- if (s == 0) then
178
- return [0, 0]
179
- elsif (s < 0)
180
- a = sqrtb_with_remainder(-x)
181
- return [ Complex(0, a[0]), a[1]]
182
- end
148
+ # handle some obvious errors with x first
149
+ raise TypeError, "non numeric 1st arg \"#{x.inspect}\"" if ! (x.kind_of? Numeric) && ! (x.kind_of? String)
150
+ # we could maybe even work with complex number, if their imaginary part is zero.
151
+ # but this is not so important to deal with, so we raise an error anyway.
152
+ raise TypeError, "complex numbers not supported \"#{x.inspect}\"" if x.kind_of? Complex
183
153
 
184
- xwords = split_to_words(x, 2)
185
- xi = xwords[0] - 1
186
- yi = 1
154
+ # handle some obvious errors with optional second parameter, if present
155
+ raise TypeError, "non integer 2nd arg \"#{s.inspect}\"" if ! s.kind_of? Integer
156
+ raise TypeError, "negative 2nd arg \"#{s.inspect}\"" if s < 0
187
157
 
188
- 1.upto(xwords.length-1) do |i|
189
- xi = (xi << 2) + xwords[i]
190
- d0 = (yi << 2) + 1
191
- r = xi - d0
192
- b = 0
193
- if (r >= 0) then
194
- b = 1
195
- xi = r
196
- end
197
- yi = (yi << 1) + b
198
- end
199
- return [yi, xi]
200
- end
158
+ # scale is the second parameter or 0 if it is missing
159
+ scale = s
160
+ # int_val is the integral value that is multiplied by some 10**-n
161
+ int_val = 0
201
162
 
202
- #
163
+ if x.kind_of? Integer then
164
+ # integers are trivial to handle
165
+ int_val = x
203
166
 
204
- # calculate the square root of an integer using larger chunks of the
205
- # number. The optional parameter n provides the size of these
206
- # chunks. It is by default chosen to be 16, which is optimized for
207
- # 32 bit systems, because internally parts of the double size are
208
- # used.
209
- # the result is rounded to an integer y such that
210
- # y**2�<=�x�<�(y+1)**2
211
- #
212
- def LongMath.sqrtw(x, n = 16)
213
- a = sqrtw_with_remainder(x, n)
214
- a[0]
215
- end
167
+ elsif x.kind_of? Rational then
168
+ # rationals are rounded somehow
169
+ # we need to come up with a better rule here.
170
+ # if denominator is any product of powers of 2 and 5, we do not need to round
171
+ denom = x.denominator
172
+ mul_2 = LongMath.multiplicity_of_factor(denom, 2)
173
+ mul_5 = LongMath.multiplicity_of_factor(denom, 5)
174
+ iscale = [mul_2, mul_5].max
175
+ scale += iscale
176
+ denom /= 2 ** mul_2
177
+ denom /= 5 ** mul_5
178
+ iscale2 = Math.log10(denom).ceil
179
+ scale += iscale2
180
+ # int_val = (x * 10 ** scale).to_i
181
+ int_val = (x * 10 ** (iscale2+iscale)).to_i
216
182
 
217
- #
218
- # calculate the an integer s >= 0 and a remainder r >= 0 such that
219
- #�x�=�s**2�+�r�and�s**2�<=�x�<�(s+1)**2
220
- # the wordwise algorithm is used, which works well for relatively
221
- # large values of x. n defines the word size to be used for the
222
- # algorithm. It is good to use half of the machine word, but the
223
- # algorithm would also work for other values.
224
- #
225
- def LongMath.sqrtw_with_remainder(x, n = 16)
226
- check_is_int(x, "x")
227
- check_is_int(n, "n")
228
- n2 = n<<1
229
- n1 = n+1
230
- check_word_len(n2, "2*n")
183
+ else
184
+ # we assume a string or a floating point number
185
+ # floating point number or BigDecimal is converted to string, so
186
+ # we only deal with strings
187
+ # this operation is not so common, so there is no urgent need to
188
+ # optimize it
189
+ num_str = x.to_s
190
+ len = num_str.length
231
191
 
232
- s = (x <=> 0)
233
- if (s == 0) then
234
- return [0, 0]
235
- elsif (s < 0)
236
- a = sqrtw_with_remainder(-x)
237
- return [ Complex(0, a[0]), a[1]]
238
- end
192
+ # handle the obvious error that string is empty
193
+ raise TypeError, "1st arg must not be empty string. \"#{num_str.inspect}\"" if len == 0
239
194
 
240
- xwords = split_to_words(x, n2)
241
- if (xwords.length == 1) then
242
- return sqrtb_with_remainder(xwords[0])
243
- end
195
+ # remove spaces and underscores
196
+ num_str.gsub! /\s/, ""
197
+ num_str.gsub! /_/, ""
244
198
 
245
- # puts(xwords.inspect + "\n")
246
- xi = (xwords[0] << n2) + xwords[1]
247
- a = sqrtb_with_remainder(xi)
248
- yi = a[0]
249
- if (xwords.length <= 2) then
250
- return a
251
- end
199
+ # handle sign
200
+ num_str.gsub! /^\+/, ""
201
+ negative = false
202
+ if num_str.gsub! /^-/, "" then
203
+ negative = true
204
+ end
252
205
 
253
- xi -= yi*yi
254
- 2.upto(xwords.length-1) do |i|
255
- xi = (xi << n2) + xwords[i]
256
- d0 = (yi << n1)
257
- q = (xi / d0).to_i
258
- q0 = q
259
- j = 0
260
- was_negative = false
261
- while (true) do
262
- d = d0 + q
263
- r = xi - (q * d)
264
- break if (0 <= r && (r < d || was_negative))
265
- # puts("i=#{i} j=#{j} q=#{q} d0=#{d0} d=#{d} r=#{r} yi=#{yi} xi=#{xi}\n")
266
- if (r < 0) then
267
- was_negative = true
268
- q = q-1
269
- else
270
- q = q+1
271
- end
272
- j += 1
273
- if (j > 10) then
274
- # puts("i=#{i} j=#{j} q=#{q} q0=#{q0} d0=#{d0} d=#{d} r=#{r} yi=#{yi} xi=#{xi}\n")
275
- break
276
- end
206
+ # split in parts before and after decimal point
207
+ num_arr = num_str.split /\./
208
+ if num_arr.length > 2 then
209
+ raise TypeError, "1st arg contains more than one . \"#{num_str.inspect}\""
210
+ end
211
+ num_int = num_arr[0]
212
+ num_rem = num_arr[1]
213
+ num_frac = nil
214
+ num_exp = nil
215
+ unless num_rem.nil? then
216
+ num_arr = num_rem.split /[Ee]/
217
+ num_frac = num_arr[0]
218
+ num_exp = num_arr[1]
277
219
  end
278
- xi = r
279
- yi = (yi << n) + q
280
- end
281
- return [ yi, xi ]
282
- end
283
220
 
284
- #
221
+ if num_frac.nil? then
222
+ num_frac = ""
223
+ end
285
224
 
286
- # find the gcd of an Integer x with b**n0 where n0 is a sufficiently
287
- # high exponent
288
- # such that gcd(x, b**m) = gcd(x, b**n) for all m, n >= n0
289
- #
290
- def LongMath.gcd_with_high_power(x, b)
291
- check_is_int(x, "x")
292
- raise ZeroDivisionError, "gcd_with_high_power of zero with \"#{b.inspect}\" would be infinity" if x.zero?
293
- check_is_int(b, "b")
294
- raise ZeroDivisionError, "gcd_with_high_power with b < 2 is not defined. b=\"#{b.inspect}\"" if b < 2
295
- s = x.abs
296
- exponent = 1
297
- b = b.abs
298
- if (b < s && s < MAX_FLOATABLE)
299
- exponent = (Math.log(s) / Math.log(b)).ceil
225
+ if num_exp.nil? || num_exp.empty? then
226
+ num_exp = "0"
227
+ end
228
+ num_exp = num_exp.to_i
229
+ iscale = num_frac.length - num_exp
230
+ scale += iscale
231
+ int_val = (num_int + num_frac).to_i
232
+ if negative then
233
+ int_val = -int_val
234
+ end
300
235
  end
301
- power = b**exponent
302
- result = 1
303
- begin
304
- f = s.gcd(power)
305
- s /= f
306
- result *= f
307
- end while f > 1
308
- result
309
- end
236
+ @scale = scale
237
+ @int_val = int_val
238
+
239
+ end # initialize
240
+
241
+ attr_reader :int_val, :scale
310
242
 
311
243
  #
312
- # Find the exponent of the highest power of prime number p that divides
313
- # the Integer x. Only works for prime numbers p (parameter prime_number).
314
- # The caller has to make sure that p (parameter prime_number) is
315
- # actually a prime number, because checks for primality actually cost
316
- # something and should not be duplicated more than necessary.
317
- # This method works even for numbers x that exceed the range of Float
244
+ # alter scale (changes self)
318
245
  #
319
- def LongMath.multiplicity_of_factor(x, prime_number)
320
-
321
- if (x.kind_of? Rational) || (x.kind_of? LongDecimalQuot) then
322
- m1 = multiplicity_of_factor(x.numerator, prime_number)
323
- m2 = multiplicity_of_factor(x.denominator, prime_number)
324
- return m1 - m2
246
+ # only for internal use:
247
+ # use round_to_scale instead
248
+ #
249
+ def scale=(s)
250
+ raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
251
+ raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
325
252
 
326
- elsif (x.kind_of? LongDecimal)
327
- m1 = multiplicity_of_factor(x.numerator, prime_number)
328
- if (prime_number == 2 || prime_number == 5) then
329
- return m1 - x.scale
253
+ # do not work too hard, if scale does not really change.
254
+ unless @scale == s then
255
+ # multiply int_val by a power of 10 in order to compensate for
256
+ # the change of scale and to keep number in the same order of magnitude.
257
+ d = s - @scale
258
+ f = 10 ** (d.abs)
259
+ if (d >= 0) then
260
+ @int_val = (@int_val * f).to_i
330
261
  else
331
- return m1
262
+ # here we actually do rounding
263
+ @int_val = (@int_val / f).to_i
332
264
  end
265
+ @scale = s
266
+ end
267
+ end
333
268
 
334
- elsif (x.kind_of? Integer)
269
+ protected :scale=
335
270
 
336
- power = gcd_with_high_power(x, prime_number)
337
- if (power.abs < MAX_FLOATABLE) then
338
- result = (Math.log(power) / Math.log(prime_number)).round
271
+ #
272
+ # create copy of self with different scale
273
+ # param1: new_scale new scale for result
274
+ # param2: mode rounding mode to be applied when information is
275
+ # lost. defaults to ROUND_UNNECESSARY, which
276
+ # means that an exception is thrown if rounding
277
+ # would actually loose any information.
278
+ #
279
+ def round_to_scale(new_scale, mode = ROUND_UNNECESSARY)
280
+
281
+ raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
282
+ raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
283
+ raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
284
+ if @scale == new_scale then
285
+ self
286
+ else
287
+ diff = new_scale - scale
288
+ factor = 10 ** (diff.abs)
289
+ if (diff > 0) then
290
+ # we become more precise, no rounding issues
291
+ new_int_val = int_val * factor
339
292
  else
340
- e = (Math.log(MAX_FLOATABLE) / Math.log(prime_number)).floor
341
- result = 0
342
- partial = prime_number ** e
343
- while (power > partial) do
344
- power /= partial
345
- result += e
293
+ quot, rem = int_val.divmod(factor)
294
+ if (rem == 0) then
295
+ new_int_val = quot
296
+ elsif (mode == ROUND_UNNECESSARY) then
297
+ raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
298
+ else
299
+ return LongDecimalQuot(self, LongDecimal(1)).round_to_scale(new_scale, mode)
346
300
  end
347
- result += (Math.log(power) / Math.log(prime_number)).round
348
301
  end
349
- return result
350
- else
351
- raise TypeError, "type of x is not supported #{x.class} #{x.inpect}"
302
+ LongDecimal(new_int_val, new_scale)
352
303
  end
353
304
  end
354
305
 
355
306
  #
356
- # method for calculating pi to the given number of digits after the
357
- # decimal point.
358
- # It works fine for 1000 or 2000 digits or so.
359
- # This method could be optimized more, but if you really want to go
360
- # for more digits, you will find a specialized and optimized program
361
- # for this specific purpose, probably written in C or C++.
362
- # Since calculation of pi is not what should typically be done with
363
- # LongDecimal, you may consider this method to be the easter egg of
364
- # LongDecimal. ;-)
365
- #
366
- def LongMath.calc_pi(prec, final_mode = LongDecimal::ROUND_HALF_DOWN)
367
- mode = LongDecimal::ROUND_HALF_DOWN
368
- iprec = 5*(prec+1)
369
- sprec = (iprec >> 1) + 1
370
- dprec = (prec+1) << 1
371
-
372
- a = LongDecimal(1)
373
- b = (1 / LongDecimal(2).sqrt(iprec,mode)).round_to_scale(iprec, mode)
374
- c = LongDecimal(5,1)
375
- k = 1
376
- pow_k = 2
377
-
378
- pi = 0
379
- last_pi = 0
380
- last_diff = 1
307
+ # convert self into String, which is the decimal representation.
308
+ # Use trailing zeros, if int_val has them.
381
309
 
382
- loop do
383
- a, b = ((a + b) / 2).round_to_scale(sprec, mode), (a * b).round_to_scale(iprec, mode).sqrt(sprec, mode)
384
- c = (c - pow_k * (a * a - b * b)).round_to_scale(iprec, mode)
385
- pi = (2 * a * a / c).round_to_scale(sprec, mode)
386
- diff = (pi - last_pi).round_to_scale(dprec, mode).abs
387
- if (diff.zero? && last_diff.zero?) then
388
- break
310
+ # optional parameter shown_scale is the number of digits after the
311
+ # decimal point. Defaults to the scale of self.
312
+ # optional parameter mode ist the rounding mode to be applied.
313
+ # Defaults to ROUND_UNNECESSARY, in which case an exception is
314
+ # thrown if rounding is actually necessary.
315
+ # optional parameter base is the base to be used when expressing
316
+ # self as string. defaults to 10.
317
+ #
318
+ def to_s(shown_scale = @scale, mode = ROUND_UNNECESSARY, base = 10)
319
+ if (base == 10) then
320
+ if (shown_scale == @scale)
321
+ to_s_10
322
+ else
323
+ s = self.round_to_scale(shown_scale, mode)
324
+ s.to_s_10
389
325
  end
390
- last_pi = pi
391
- last_diff = diff
392
- k += 1
393
- pow_k = pow_k << 1
394
- # puts("k=#{k} pi=#{pi.to_s}\nd=#{diff}\n\n")
326
+ else
327
+ # base is not 10
328
+ unless (base.kind_of? Integer) && 2 <= base && base <= 36 then
329
+ raise TypeError, "base must be integer between 2 and 36"
330
+ end
331
+ quot = (self.move_point_right(scale) * base ** shown_scale) / 10 ** scale
332
+ # p(quot)
333
+ rounded = quot.round_to_scale(0, mode)
334
+ # p(rounded)
335
+ rounded.to_s_internal(base, shown_scale)
395
336
  end
396
- pi.round_to_scale(prec, final_mode)
397
337
  end
398
338
 
399
339
  #
400
- # calc the exponential function of x to the given precision as
401
- # LongDecimal. Only supports values of x such that the result still
402
- # fits into a float (x <= 709). This limitation is somewhat
403
- # arbitrary, but it is enforced in order to avoid producing numbers
404
- # with the exponential function that exceed the memory. It may be
405
- # removed in future versions.
340
+ # internal helper method, converts self to string in decimal system
341
+ # with default settings.
406
342
  #
407
- def LongMath.exp(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
408
- check_is_ld(x, "x")
409
- raise TypeError, "x=#{x.inspect} must not be greater #{MAX_EXP_ABLE}" unless x <= MAX_EXP_ABLE
410
- check_is_prec(prec, "prec")
411
- check_is_mode(mode, "mode")
412
- exp_internal(x, prec, mode)
343
+ def to_s_10
344
+ to_s_internal(10, scale)
413
345
  end
414
346
 
415
347
  #
416
- # private helper method for exponentiation
417
- # calculate internal precision
348
+ # internal helper method, converts self to string in any number system
418
349
  #
419
- def LongMath.calc_iprec_for_exp(x, prec)
420
- iprec_extra = 0
421
- if (x > 1) then
422
- xf = x.to_f
423
- iprec_extra = (xf / LOG10).abs
424
- end
425
- iprec = ((prec+10)*1.20 + iprec_extra).round
426
- if (iprec < prec) then
427
- iprec = prec
350
+ def to_s_internal(b, sc)
351
+ sg = sgn
352
+ i = int_val.abs
353
+ str = i.to_s(b)
354
+ if sc > 0 then
355
+ missing = sc - str.length + 1
356
+ if missing > 0 then
357
+ str = ("0" * missing) + str
358
+ end
359
+ str[-sc, 0] = "."
428
360
  end
429
- # puts("calc_iprec_for_exp: x=#{x} prec=#{prec} iprec=#{iprec} iprec_extra=#{iprec_extra}\n")
430
- iprec
361
+ str = "-" + str if sg < 0
362
+ str
431
363
  end
432
364
 
433
- # private :calc_iprec_for_exp
365
+ protected :to_s_10
366
+ protected :to_s_internal
434
367
 
435
368
  #
436
- # internal functionality of exp. exposes some more parameters, that
437
- # should usually be set to defaut values, in order to allow better testing.
438
- # do not actually call this method unless you are testing exp.
439
- # create a bug report, if the default settings for the parameters do
440
- # not work correctly
369
+ # convert self into Rational
370
+ # this works quite straitforward. use int_val as numerator and a
371
+ # power of 10 as denominator
441
372
  #
442
- def LongMath.exp_internal(x, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, j = nil, k = nil, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
443
- check_is_ld(x, "x")
444
- if (prec == nil) then
445
- prec = x.scale
446
- end
447
- check_is_prec(prec, "prec")
448
-
449
- if (final_mode == nil)
450
- final_mode = LongDecimal::ROUND_HALF_DOWN
451
- end
452
- check_is_mode(final_mode, "final_mode")
453
- check_is_mode(mode, "mode")
454
-
455
- # if the result would come out to zero anyway, cut the work
456
- xi = x.to_i
457
- if (xi < -LongMath::MAX_FLOATABLE) || -((xi.to_f - 1) / LOG10) > prec+1 then
458
- return LongDecimal(25, prec+2).round_to_scale(prec, final_mode)
459
- end
460
-
461
- if j == nil || k == nil then
462
- s1 = (prec * LOG10 / LOG2) ** (1.0/3.0)
463
- if (j == nil) then
464
- j = s1.round
465
- end
466
- if (k == nil) then
467
- k = (s1 + Math.log([1, prec].max) / LOG2).round
468
- end
469
- if (x > 1) then
470
- k += (Math.log(x.to_f) / LOG2).abs.round
471
- end
472
- end
473
- if (j <= 0) then
474
- j = 1
475
- end
476
- if (k < 0) then
477
- k = 0
478
- end
479
- check_is_int(j, "j")
480
- check_is_int(k, "k")
481
-
482
- if (iprec == nil) then
483
- iprec = calc_iprec_for_exp(x, prec)
484
- end
485
- check_is_prec(iprec, "iprec")
486
- # puts("exp_internal: x=#{x} prec=#{prec} iprec=#{iprec}\n")
487
-
488
- dprec = [ iprec, (prec + 1) << 1 ].min
373
+ def to_r
374
+ Rational(numerator, denominator)
375
+ end
489
376
 
490
- x_k = (x / (1 << k)).round_to_scale(iprec, mode)
491
- x_j = (x_k ** j).round_to_scale(iprec, mode)
492
- s = [ LongDecimal(0) ] * j
493
- t = LongDecimal(1)
494
- last_t = 1
495
- f = 0
496
- loop do
497
- j.times do |i|
498
- s[i] += t
499
- f += 1
500
- t = (t / f).round_to_scale(iprec, mode)
501
- end
502
- t = (t * x_j).round_to_scale(iprec, mode)
503
- break if (t.zero?)
504
- tr = t.round_to_scale(dprec, LongDecimal::ROUND_DOWN).abs
505
- break if (t.zero?)
506
- tu = t.unit
507
- break if (tr <= tu && last_t <= tu)
508
- last_t = tr
509
- end
510
- x_i = 1
511
- y_k = LongDecimal(0)
512
- j.times do |i|
513
- if (i > 0) then
514
- x_i = (x_i * x_k).round_to_scale(iprec, mode)
377
+ #
378
+ # convert self into Float
379
+ # this works straitforward by dividing int_val by power of 10 in
380
+ # float-arithmetic, in all cases where numerator and denominator are
381
+ # within the ranges expressable as Floats. Goes via string
382
+ # representation otherwise.
383
+ #
384
+ def to_f
385
+ divisor = denominator
386
+ if (divisor == 1) then
387
+ return numerator.to_f
388
+ elsif int_val.abs <= LongMath::MAX_FLOATABLE then
389
+ if (divisor.abs > LongMath::MAX_FLOATABLE) then
390
+ return 0.0
391
+ else
392
+ f = int_val.to_f
393
+ return f / divisor
515
394
  end
516
- # puts("y_k=#{y_k}\ni=#{i} j=#{j} k=#{k} x=#{x}\nx_k=#{x_k}\nx_j=#{x_j}\nx_i=#{x_i}\ns[i]=#{s[i]}\n\n")
517
- y_k += (s[i] * x_i).round_to_scale(iprec, mode)
518
- end
519
- # puts("y_k = #{y_k}\n")
520
- k.times do |i|
521
- y_k = y_k.square.round_to_scale(iprec, mode)
522
- # puts("i=#{i} y_k = #{y_k}\n")
395
+ elsif numerator.abs < divisor
396
+ # self is between -1 and 1
397
+ # factor = numerator.abs.div(LongMath::MAX_FLOATABLE)
398
+ # digits = factor.to_ld.int_digits10
399
+ # return LongDecimal(numerator.div(10**digits), scale -digits).to_f
400
+ return self.to_s.to_f
401
+ else
402
+ # s2 = [scale.div(2), 1].max
403
+ # return LongDecimal(numerator.div(10**s2), scale - s2).to_f
404
+ return self.to_s.to_f
523
405
  end
524
- y = y_k.round_to_scale(prec, final_mode)
525
- y
526
406
  end
527
407
 
528
408
  #
529
- # calculate the natural logarithm function of x to the given precision as
530
- # LongDecimal.
409
+ # convert self into Integer
410
+ # This may loose information. In most cases it is preferred to
411
+ # control this by calling round_to_scale first and then applying
412
+ # to_i when the number represented by self is actually an integer.
531
413
  #
532
- def LongMath.log(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
533
- check_is_ld(x, "x")
534
- check_is_prec(prec, "prec")
535
- check_is_mode(mode, "mode")
536
- log_internal(x, prec, mode)
414
+ def to_i
415
+ numerator.div(denominator)
537
416
  end
538
417
 
539
418
  #
540
- # calculate the base 10 logarithm of x to the given precision as
541
- # LongDecimal.
419
+ # convert self into LongDecimal (returns self)
542
420
  #
543
- def LongMath.log10(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
544
- check_is_ld(x, "x")
545
- check_is_prec(prec, "prec")
546
- if (x.one?) then
547
- return LongDecimal.zero!(prec)
548
- end
549
- check_is_mode(mode, "mode")
550
- iprec = prec + 2
551
- id = x.int_digits10
552
- xx = x.move_point_left(id)
553
- # puts("x=#{x} xx=#{xx} id=#{id} iprec=#{iprec}\n")
554
- lnxx = log_internal(xx, iprec, mode)
555
- ln10 = log_internal(10.to_ld, iprec, mode)
556
- y = id + (lnxx / ln10).round_to_scale(prec, mode)
557
- return y
421
+ def to_ld
422
+ self
558
423
  end
559
424
 
560
425
  #
561
- # calculate the base 2 logarithm of x to the given precision as
562
- # LongDecimal.
426
+ # convert selt into BigDecimal
563
427
  #
564
- def LongMath.log2(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
565
- check_is_ld(x, "x")
566
- check_is_prec(prec, "prec")
567
- if (x.one?) then
568
- return LongDecimal.zero!(prec)
569
- end
570
- check_is_mode(mode, "mode")
571
- iprec = prec + 2
572
- id = x.int_digits2
573
- xx = (x / (1 << id)).round_to_scale(x.scale+id)
574
- # puts("x=#{x} xx=#{xx} id=#{id} iprec=#{iprec}\n")
575
- lnxx = log_internal(xx, iprec, mode)
576
- ln2 = log_internal(2.to_ld, iprec, mode)
577
- y = id + (lnxx / ln2).round_to_scale(prec, mode)
578
- return y
428
+ def to_bd
429
+ # this operation is probably not used so heavily, so we can live with a
430
+ # string as an intermediate step.
431
+ BigDecimal(self.to_s)
579
432
  end
580
433
 
581
434
  #
582
- # internal functionality of log. exposes some more parameters, that
583
- # should usually be set to defaut values, in order to allow better testing.
584
- # do not actually call this method unless you are testing log.
585
- # create a bug report, if the default settings for the parameters do
586
- # not work correctly
435
+ # LongDecimals can be seen as a fraction with a power of 10 as
436
+ # denominator for compatibility with other numeric classes this
437
+ # method is included, returning 10**scale.
438
+ # Please observe that there may be common factors of numerator and
439
+ # denominator in case of LongDecimal, which does not occur in case
440
+ # of Rational
587
441
  #
588
- def LongMath.log_internal(x, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
589
- check_is_ld(x)
590
- raise TypeError, "x=#{x.inspect} must not be positive" unless x > 0
591
- if (prec == nil) then
592
- prec = x.scale
593
- end
594
- check_is_prec(prec, "prec")
595
- if (x.one?) then
596
- return LongDecimal.zero!(prec)
597
- end
442
+ def denominator
443
+ 10**scale
444
+ end
598
445
 
599
- if (final_mode == nil)
600
- final_mode = LongDecimal::ROUND_HALF_DOWN
601
- end
602
- check_is_mode(final_mode, "final_mode")
603
- check_is_mode(mode, "mode")
446
+ #
447
+ # LongDecimals can be seen as a fraction with its int_val as its
448
+ # numerator
449
+ # Please observe that there may be common factors of numerator and
450
+ # denominator in case of LongDecimal, which does not occur in case
451
+ # of Rational
452
+ #
453
+ alias numerator int_val
604
454
 
605
- if (iprec == nil) then
606
- iprec = ((prec+10)*1.20).round
455
+ #
456
+ # number of binary digits before the decimal point, not counting a single 0.
457
+ # 0.xxx -> 0
458
+ # 1.xxx -> 1
459
+ # 2.xxx -> 2
460
+ # 4.xxx -> 3
461
+ # 8.xxx -> 4
462
+ # ...
463
+ #
464
+ def int_digits2
465
+ int_part = self.abs.to_i
466
+ if int_part.zero? then
467
+ return 0
607
468
  end
608
- if (iprec < prec) then
609
- iprec = prec
469
+
470
+ n = int_part.size * 8 - 31
471
+ int_part = int_part >> n
472
+ until int_part.zero? do
473
+ int_part = int_part >> 1
474
+ n += 1
610
475
  end
611
- check_is_prec(iprec, "iprec")
476
+ n
477
+ end
612
478
 
613
- # dprec = [ iprec - 1, (prec + 1) << 1 ].min
614
- dprec = iprec - 1
479
+ #
480
+ # number of decimal digits before the decimal point, not counting a
481
+ # single 0.
482
+ # 0.xxx -> 0
483
+ # 1.xxx -> 1
484
+ # 10.xxx -> 2
485
+ # ...
486
+ #
487
+ def int_digits10
488
+ int_part = self.abs.to_i
489
+ if int_part.zero? then
490
+ return 0
491
+ end
615
492
 
616
- y = 0
617
- s = 1
618
- if (x < 1) then
619
- # puts("x=#{x} iprec=#{iprec}\n")
620
- x = (1 / x).round_to_scale(iprec, mode)
621
- s = -1
622
- # puts("s=#{s} x=#{x} iprec=#{iprec}\n")
493
+ id = 1
494
+ powers = []
495
+ power = 10
496
+ idx = 0
497
+ until int_part.zero? do
498
+ expon = 1 << idx
499
+ powers[idx] = power
500
+ break if int_part < power
501
+ id += expon
502
+ int_part = (int_part / power).to_i
503
+ idx += 1
504
+ power = power * power
623
505
  end
624
- exp_part = 0
625
- estimate = 0
626
- while (x > MAX_FLOATABLE) do
627
- if (exp_part == 0) then
628
- estimate = MAX_EXP_ABLE.to_ld
629
- exp_part = exp(estimate, iprec)
630
- end
631
- x = (x / exp_part).round_to_scale(iprec, mode)
632
- if (s < 0) then
633
- y -= estimate
634
- else
635
- y += estimate
506
+ until int_part < 10 do
507
+ idx -= 1
508
+ expon = 1 << idx
509
+ power = powers[idx]
510
+ # puts("i=#{int_part} p=#{power}\n")
511
+ while int_part >= power
512
+ id += expon
513
+ int_part = (int_part / power).to_i
636
514
  end
637
515
  end
516
+ id
517
+ end
638
518
 
639
- delta = LongDecimal(1, 3)
640
- while (x - 1).abs > delta do
641
- # puts("too far from 1: x=#{x}\n")
642
- xf = x.to_f
643
- # puts("xf=#{xf}\n")
644
- mlx = Math.log(xf)
645
- # puts("log(xf)=#{mlx}\n")
646
- estimate = mlx.to_ld.round_to_scale(20, mode)
647
- exp_part = exp(estimate, iprec << 1)
648
- # puts("y=#{y} s=#{s} est=#{estimate} part=#{exp_part} x=#{x}\n")
649
- x = (x / exp_part).round_to_scale(iprec, mode)
650
- # puts("divided by exp_part=#{exp_part}: #{x}\n")
651
- if (s < 0) then
652
- y -= estimate
519
+ #
520
+ # before adding or subtracting two LongDecimal numbers
521
+ # it is mandatory to set them to the same scale. The maximum of the
522
+ # two summands is used, in order to avoid loosing any information.
523
+ # this method is mostly for internal use
524
+ #
525
+ def equalize_scale(other)
526
+ o, s = coerce(other)
527
+ if (s.kind_of? LongDecimal) then
528
+ # make sure Floats do not mess up our number of significant digits when adding
529
+ if (other.kind_of? Float) then
530
+ o = o.round_to_scale(s.scale, ROUND_HALF_UP)
653
531
  else
654
- y += estimate
532
+ new_scale = [s.scale, o.scale].max
533
+ s = s.round_to_scale(new_scale)
534
+ o = o.round_to_scale(new_scale)
655
535
  end
656
- # puts("y=#{y} s=#{s} est=#{estimate} part=#{exp_part} x=#{x}\n")
657
536
  end
537
+ return s, o
538
+ end
539
+
540
+ #
541
+ # before dividing two LongDecimal numbers, it is mandatory to set
542
+ # make them both to integers, so the result is simply expressable as
543
+ # a rational
544
+ # this method is mostly for internal use
545
+ #
546
+ def anti_equalize_scale(other)
547
+ o, s = coerce(other)
548
+ if (s.kind_of? LongDecimal) then
549
+ exponent = [s.scale, o.scale].max
550
+ factor = 10**exponent
551
+ s *= factor
552
+ o *= factor
553
+ s = s.round_to_scale(0)
554
+ o = o.round_to_scale(0)
555
+ end
556
+ return s, o
557
+ end
658
558
 
659
- factor = 1
660
- # delta = LongDecimal(1, (iprec.to_f**(1/3)).round)
661
- # while (x - 1).abs > delta do
662
- # x = sqrt(x)
663
- # factor *= 2
664
- # end
559
+ #
560
+ # successor as needed for using LongDecimal in ranges
561
+ # it needs to be observed that this is usually not an increment by
562
+ # 1, but by 1/10**scale.
563
+ #
564
+ def succ
565
+ LongDecimal(int_val + 1, scale)
566
+ end
665
567
 
666
- sum = 0
667
- z = 1 - x
668
- i = 1
669
- p = 1.to_ld
670
- d = 1.to_ld
671
- until p.abs.round_to_scale(dprec, LongDecimal::ROUND_DOWN).zero? do
672
- p = (p * z).round_to_scale(iprec, mode)
673
- d = (p / i).round_to_scale(iprec, mode)
674
- i += 1
675
- sum += d
568
+ alias next succ
676
569
 
677
- # puts("log_internal: s=#{sum} d=#{d} x=#{x} i=#{i} p=#{p} iprec=#{iprec} dprec=#{dprec}\n") if (i & 0x0f == 0x0f)
678
- end
570
+ #
571
+ # predecessor (opposite of successor)
572
+ # it needs to be observed that this is usually not an decrement by
573
+ # 1, but by 1/10**scale.
574
+ #
575
+ def pred
576
+ LongDecimal(int_val - 1, scale)
577
+ end
679
578
 
680
- # puts("y=#{y} s=#{s} f=#{factor} sum=#{sum}\n")
681
- y -= ((s * factor) * sum).round_to_scale(iprec, mode)
682
- # puts("y=#{y} s=#{s} f=#{factor} sum=#{sum}\n")
683
- return y.round_to_scale(prec, final_mode)
579
+ #
580
+ # self + 1
581
+ #
582
+ def inc
583
+ self + 1
584
+ end
684
585
 
586
+ #
587
+ # self - 1
588
+ #
589
+ def dec
590
+ self - 1
685
591
  end
686
592
 
687
593
  #
688
- # calc the power of x with exponent y to the given precision as
689
- # LongDecimal. Only supports values of y such that exp(y) still
690
- # fits into a float (y <= 709)
594
+ # self += 1
691
595
  #
692
- def LongMath.power(x, y, prec, mode = LongDecimal::ROUND_HALF_DOWN)
693
- check_is_ld(x, "x")
694
- check_is_ld(y, "y")
695
- raise TypeError, "y=#{y.inspect} must not be greater #{MAX_EXP_ABLE}" unless y <= MAX_EXP_ABLE
696
- raise TypeError, "x=#{x.inspect} must not be greater #{MAX_FLOATABLE}" unless x <= MAX_FLOATABLE
697
- raise TypeError, "x=#{x.inspect} must not positive" unless x > 0
698
- check_is_prec(prec, "prec")
699
- check_is_mode(mode, "mode")
700
- LongMath.power_internal(x, y, prec, mode)
596
+ def inc!
597
+ @int_val += denominator
701
598
  end
702
599
 
703
600
  #
704
- # internal functionality of exp. exposes some more parameters, that
705
- # should usually be set to defaut values, in order to allow better testing.
706
- # do not actually call this method unless you are testing exp.
707
- # create a bug report, if the default settings for the parameters do
708
- # not work correctly
601
+ # self -= 1
709
602
  #
710
- def LongMath.power_internal(x, y, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
711
- check_is_ld(x, "x")
712
- if (prec == nil) then
713
- prec = x.scale
714
- end
715
- check_is_prec(prec, "prec")
603
+ def dec!
604
+ @int_val -= denominator
605
+ end
716
606
 
717
- if (final_mode == nil)
718
- final_mode = LongDecimal::ROUND_HALF_DOWN
719
- end
720
- check_is_mode(final_mode, "final_mode")
721
- check_is_mode(mode, "mode")
607
+ #
608
+ # return the unit by which self is incremented by succ
609
+ #
610
+ def unit
611
+ LongDecimal(1, scale)
612
+ end
722
613
 
723
- logx_y_f = Math.log(x.to_f) * (y.to_f)
614
+ #
615
+ # apply unary +
616
+ # (returns self)
617
+ #
618
+ def +@
619
+ self
620
+ end
724
621
 
725
- # iprec = (prec * 1.2 + 20 + (y.abs.to_f) * 1.5 * x.int_digits2).round
726
- if (iprec == nil) then
727
- iprec = calc_iprec_for_exp(logx_y_f, prec) + 2
622
+ #
623
+ # apply unary -
624
+ # (returns negated self)
625
+ #
626
+ def -@
627
+ if self.zero? then
628
+ self
629
+ else
630
+ LongDecimal(-int_val, scale)
728
631
  end
729
- # puts("power_internal: x=#{x} y=#{y} logx_y=#{logx_y_f} iprec=#{iprec} prec=#{prec}\n")
730
- logx = log(x, iprec, mode)
731
- logx_y = logx*y
732
- xy = exp_internal(logx_y, prec + 1, mode)
733
- # puts("power_internal: x=#{x} logx=#{logx} y=#{y} logx_y=#{logx_y} xy=#{xy} iprec=#{iprec} prec=#{prec}\n")
734
- xy.round_to_scale(prec, final_mode)
735
632
  end
736
633
 
737
- end # LongMath
738
-
739
- #
740
- # class for holding fixed point long decimal numbers
741
- # these can be considered as a pair of two integer. One contains the
742
- # digits and the other one the position of the decimal point.
743
- #
744
- class LongDecimal < Numeric
745
- @RCS_ID='-$Id: long-decimal.rb,v 1.2 2006/03/19 11:17:55 bk1 Exp $-'
746
-
747
- include LongDecimalRoundingMode
634
+ #
635
+ # add two numbers
636
+ # if both can immediately be expressed as LongDecimal, the result is
637
+ # a LongDecimal as well. The number of digits after the decimal
638
+ # point is the max of the scales of the summands
639
+ # if LongDecimal does not cover the two summands, call addition of
640
+ # Complex, Float or LongRationalQuot
641
+ #
642
+ def +(other)
643
+ s, o = equalize_scale(other)
644
+ if s.kind_of? LongDecimal then
645
+ LongDecimal(s.int_val + o.int_val, s.scale)
646
+ else
647
+ s + o
648
+ end
649
+ end
748
650
 
749
- # MINUS_ONE = LongDecimal(-1)
750
- # ZERO = LongDecimal(0)
751
- # ONE = LongDecimal(1)
752
- # TWO = LongDecimal(2)
753
- # TEN = LongDecimal(10)
651
+ #
652
+ # subtract two numbers
653
+ # if both can immediately be expressed as LongDecimal, the result is
654
+ # a LongDecimal as well. The number of digits after the decimal
655
+ # point is the max of the scales of self and other.
656
+ # if LongDecimal does not cover self and other, the subtraction of
657
+ # Complex, Float or LongRationalQuot is used
658
+ #
659
+ def -(other)
660
+ s, o = equalize_scale(other)
661
+ if s.kind_of? LongDecimal then
662
+ LongDecimal(s.int_val - o.int_val, s.scale)
663
+ else
664
+ s - o
665
+ end
666
+ end
754
667
 
755
668
  #
756
- # initialization
757
- # parameters:
758
- # 1. LongDecimal.new!(x) where x is a string or a number
759
- # the resulting LongDecimal holds the number x, possibly rounded
760
- # 2. LongDecimal.new!(x, s) where x is a string or a number and s is the scale
761
- # the resulting LongDecimal holds the number x / 10**s
669
+ # multiply two numbers
670
+ # if both can immediately be expressed as LongDecimal, the result is
671
+ # a LongDecimal as well. The number of digits after the decimal
672
+ # point is the sum of the scales of both factors.
673
+ # if LongDecimal does not cover self and other, the multiplication of
674
+ # Complex, Float or LongRationalQuot is used
762
675
  #
763
- def LongDecimal.new!(x, s = 0)
764
- new(x, s)
676
+ def *(other)
677
+ o, s = coerce(other)
678
+ if s.kind_of? LongDecimal then
679
+ LongDecimal(s.int_val * o.int_val, s.scale + o.scale)
680
+ else
681
+ s * o
682
+ end
765
683
  end
766
684
 
767
685
  #
768
- # creates a LongDecimal representing zero with the given number of
769
- # digits after the decimal point (scale=s)
686
+ # divide self by other and round result to scale of self using the
687
+ # given rounding mode
770
688
  #
771
- def LongDecimal.zero!(s = 0)
772
- new(0, s)
689
+ def divide(other, rounding_mode)
690
+ divide_s(other, nil, rounding_mode)
773
691
  end
774
692
 
693
+ #
694
+ # divide self by other and round result to new_scale using the
695
+ # given rounding mode. If new_scale is nil, use scale of self.
696
+ #
697
+ def divide_s(other, new_scale, rounding_mode)
698
+ q = self / other
699
+ if (q.kind_of? Float) then
700
+ q = LongDecimal(q)
701
+ end
702
+ if (q.kind_of? LongDecimal) || (q.kind_of? LongDecimalQuot) then
703
+ if (new_scale.nil?) then
704
+ new_scale = q.scale
705
+ end
706
+ q.round_to_scale(new_scale, rounding_mode)
707
+ else
708
+ q
709
+ end
710
+ end
775
711
 
776
712
  #
777
- # creates a LongDecimal representing one with the given number of
778
- # digits after the decimal point (scale=s)
713
+ # divide self by other and return result as Rational, if other
714
+ # allowed exact calculations.
779
715
  #
780
- def LongDecimal.one!(s = 0)
781
- new(10**s, s)
716
+ def rdiv(other)
717
+ q = self / other
718
+ if (q.kind_of? LongDecimalQuot) then
719
+ q.to_r
720
+ else
721
+ q
722
+ end
782
723
  end
783
724
 
725
+ #
726
+ # divide self by other and return result as LongDecimalQuot
727
+ # because division does not have an obvious rounding rule like
728
+ # addition, subtraction and multiplication, the result needs to be
729
+ # rounded afterwards to become a LongDecimal again. This way
730
+ # calculations can still be done in the natural readable way using +,
731
+ # -, *, and /, but the rounding can be provided later.
732
+ # It is very important in complicated calculations put the rounding
733
+ # steps in the right places, usually after having performed a division.
734
+ #
735
+ def /(other)
736
+ o, s = coerce(other)
737
+ if (s.kind_of? LongDecimal) then
738
+ LongDecimalQuot(s, o)
739
+ else
740
+ s / o
741
+ end
742
+ end
784
743
 
785
744
  #
786
- # creates a LongDecimal representing two with the given number of
787
- # digits after the decimal point (scale=s)
745
+ # power of self (LongDecimal) with other.
746
+ # if other is expressable as non-negative integer, the power is what
747
+ # would be obtained by successive multiplications.
748
+ # if other is expressable as negative integer, the power is a
749
+ # LongDecimalQuot as would result by successive division, but with
750
+ # the same scale as the positive power would get. Explicit rounding
751
+ # is needed to convert into a LongDecimal again
752
+ # in all other cases, self is converted into a Rational prior to
753
+ # applying power, usually resulting in a Float as power.
788
754
  #
789
- def LongDecimal.two!(s = 0)
790
- new(2*10**s, s)
755
+ def **(other)
756
+ if ((other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)) && other.is_int? then
757
+ other = other.to_i
758
+ end
759
+ if other.kind_of? Integer then
760
+ if other >= 0 then
761
+ LongDecimal(int_val ** other, scale * other)
762
+ else
763
+ abs_other = -other
764
+ new_scale = abs_other * scale
765
+ LongDecimalQuot(Rational(10 ** new_scale, int_val ** abs_other), new_scale)
766
+ end
767
+ else
768
+ if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
769
+ other = other.to_r
770
+ end
771
+ self.to_r ** other
772
+ end
791
773
  end
792
774
 
793
-
794
775
  #
795
- # creates a LongDecimal representing ten with the given number of
796
- # digits after the decimal point (scale=s)
776
+ # do integer division with remainder, returning two values
797
777
  #
798
- def LongDecimal.ten!(s = 0)
799
- new(10**(s+1), s)
778
+ def divmod(other)
779
+ if (other.kind_of? Complex) then
780
+ raise TypeError, "divmod not supported for Complex"
781
+ end
782
+ q = (self / other).to_i
783
+ return q, self - other * q
800
784
  end
801
785
 
802
-
803
786
  #
804
- # creates a LongDecimal representing minus one with the given number of
805
- # digits after the decimal point (scale=s)
787
+ # remainder of integer division by other
806
788
  #
807
- def LongDecimal.minus_one!(s = 0)
808
- new(-1*10**s, s)
789
+ def %(other)
790
+ q, r = divmod other
791
+ r
809
792
  end
810
793
 
811
-
812
794
  #
813
- # creates a LongDecimal representing a power of ten with the given
814
- # exponent e and with the given number of digits after the decimal
815
- # point (scale=s)
795
+ # performs bitwise AND between self and other
816
796
  #
817
- def LongDecimal.power_of_ten!(e, s = 0)
818
- LongMath.check_is_int(e, "e")
819
- raise TypeError, "negative 1st arg \"#{e.inspect}\"" if e < 0
820
- new(10**(s+e), s)
797
+ def &(other)
798
+ s, o = equalize_scale(other)
799
+ if s.kind_of? LongDecimal then
800
+ LongDecimal(s.int_val & o.int_val, s.scale)
801
+ else
802
+ s & o
803
+ end
821
804
  end
822
805
 
823
-
824
806
  #
825
- # initialization
826
- # parameters:
827
- # LongDecimal.new(x, s) where x is a string or a number and s is the scale
828
- # the resulting LongDecimal holds the number x / 10**s
807
+ # performs bitwise OR between self and other
829
808
  #
830
- def initialize(x, s)
831
-
832
- # handle some obvious errors with x first
833
- raise TypeError, "non numeric 1st arg \"#{x.inspect}\"" if ! (x.kind_of? Numeric) && ! (x.kind_of? String)
834
- # we could maybe even work with complex number, if their imaginary part is zero.
835
- # but this is not so important to deal with, so we raise an error anyway.
836
- raise TypeError, "complex numbers not supported \"#{x.inspect}\"" if x.kind_of? Complex
837
-
838
- # handle some obvious errors with optional second parameter, if present
839
- raise TypeError, "non integer 2nd arg \"#{s.inspect}\"" if ! s.kind_of? Integer
840
- raise TypeError, "negative 2nd arg \"#{s.inspect}\"" if s < 0
841
-
842
- # scale is the second parameter or 0 if it is missing
843
- scale = s
844
- # int_val is the integral value that is multiplied by some 10**-n
845
- int_val = 0
846
-
847
- if x.kind_of? Integer then
848
- # integers are trivial to handle
849
- int_val = x
850
-
851
- elsif x.kind_of? Rational then
852
- # rationals are rounded somehow
853
- # we need to come up with a better rule here.
854
- # if denominator is any product of powers of 2 and 5, we do not need to round
855
- denom = x.denominator
856
- mul_2 = LongMath.multiplicity_of_factor(denom, 2)
857
- mul_5 = LongMath.multiplicity_of_factor(denom, 5)
858
- iscale = [mul_2, mul_5].max
859
- scale += iscale
860
- denom /= 2 ** mul_2
861
- denom /= 5 ** mul_5
862
- iscale2 = Math.log10(denom).ceil
863
- scale += iscale2
864
- # int_val = (x * 10 ** scale).to_i
865
- int_val = (x * 10 ** (iscale2+iscale)).to_i
866
-
809
+ def |(other)
810
+ s, o = equalize_scale(other)
811
+ if s.kind_of? LongDecimal then
812
+ LongDecimal(s.int_val | o.int_val, s.scale)
867
813
  else
868
- # we assume a string or a floating point number
869
- # floating point number or BigDecimal is converted to string, so
870
- # we only deal with strings
871
- # this operation is not so common, so there is no urgent need to
872
- # optimize it
873
- num_str = x.to_s
874
- len = num_str.length
875
-
876
- # handle the obvious error that string is empty
877
- raise TypeError, "1st arg must not be empty string. \"#{num_str.inspect}\"" if len == 0
878
-
879
- # remove spaces and underscores
880
- num_str.gsub! /\s/, ""
881
- num_str.gsub! /_/, ""
882
-
883
- # handle sign
884
- num_str.gsub! /^\+/, ""
885
- negative = false
886
- if num_str.gsub! /^-/, "" then
887
- negative = true
888
- end
889
-
890
- # split in parts before and after decimal point
891
- num_arr = num_str.split /\./
892
- if num_arr.length > 2 then
893
- raise TypeError, "1st arg contains more than one . \"#{num_str.inspect}\""
894
- end
895
- num_int = num_arr[0]
896
- num_rem = num_arr[1]
897
- num_frac = nil
898
- num_exp = nil
899
- unless num_rem.nil? then
900
- num_arr = num_rem.split /[Ee]/
901
- num_frac = num_arr[0]
902
- num_exp = num_arr[1]
903
- end
904
-
905
- if num_frac.nil? then
906
- num_frac = ""
907
- end
908
-
909
- if num_exp.nil? || num_exp.empty? then
910
- num_exp = "0"
911
- end
912
- num_exp = num_exp.to_i
913
- iscale = num_frac.length - num_exp
914
- scale += iscale
915
- int_val = (num_int + num_frac).to_i
916
- if negative then
917
- int_val = -int_val
918
- end
814
+ s | o
919
815
  end
920
- @scale = scale
921
- @int_val = int_val
922
-
923
- end # initialize
924
-
925
- attr_reader :int_val, :scale
816
+ end
926
817
 
927
818
  #
928
- # alter scale (changes self)
929
- #
930
- # only for internal use:
931
- # use round_to_scale instead
819
+ # performs bitwise XOR between self and other
932
820
  #
933
- def scale=(s)
934
- raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
935
- raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
936
-
937
- # do not work too hard, if scale does not really change.
938
- unless @scale == s then
939
- # multiply int_val by a power of 10 in order to compensate for
940
- # the change of scale and to keep number in the same order of magnitude.
941
- d = s - @scale
942
- f = 10 ** (d.abs)
943
- if (d >= 0) then
944
- @int_val = (@int_val * f).to_i
945
- else
946
- # here we actually do rounding
947
- @int_val = (@int_val / f).to_i
948
- end
949
- @scale = s
821
+ def ^(other)
822
+ s, o = equalize_scale(other)
823
+ if s.kind_of? LongDecimal then
824
+ LongDecimal(s.int_val ^ o.int_val, s.scale)
825
+ else
826
+ s ^ o
950
827
  end
951
828
  end
952
829
 
953
- protected :scale=
954
-
955
830
  #
956
- # create copy of self with different scale
957
- # param1: new_scale new scale for result
958
- # param2: mode rounding mode to be applied when information is
959
- # lost. defaults to ROUND_UNNECESSARY, which
960
- # means that an exception is thrown if rounding
961
- # would actually loose any information.
831
+ # bitwise inversion
962
832
  #
963
- def round_to_scale(new_scale, mode = ROUND_UNNECESSARY)
964
-
965
- raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
966
- raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
967
- raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
968
- if @scale == new_scale then
969
- self
970
- else
971
- diff = new_scale - scale
972
- factor = 10 ** (diff.abs)
973
- if (diff > 0) then
974
- # we become more precise, no rounding issues
975
- new_int_val = int_val * factor
976
- else
977
- quot, rem = int_val.divmod(factor)
978
- if (rem == 0) then
979
- new_int_val = quot
980
- elsif (mode == ROUND_UNNECESSARY) then
981
- raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
982
- else
983
- return LongDecimalQuot(self, LongDecimal(1)).round_to_scale(new_scale, mode)
984
- end
985
- end
986
- LongDecimal(new_int_val, new_scale)
987
- end
833
+ def ~
834
+ LongDecimal(~int_val, scale)
988
835
  end
989
836
 
990
837
  #
991
- # convert self into String, which is the decimal representation.
992
- # Use trailing zeros, if int_val has them.
993
-
994
- # optional parameter shown_scale is the number of digits after the
995
- # decimal point. Defaults to the scale of self.
996
- # optional parameter mode ist the rounding mode to be applied.
997
- # Defaults to ROUND_UNNECESSARY, in which case an exception is
998
- # thrown if rounding is actually necessary.
999
- # optional parameter base is the base to be used when expressing
1000
- # self as string. defaults to 10.
838
+ # performs bitwise left shift of self by other
1001
839
  #
1002
- def to_s(shown_scale = @scale, mode = ROUND_UNNECESSARY, base = 10)
1003
- if (base == 10) then
1004
- if (shown_scale == @scale)
1005
- to_s_10
1006
- else
1007
- s = self.round_to_scale(shown_scale, mode)
1008
- s.to_s_10
1009
- end
1010
- else
1011
- # base is not 10
1012
- unless (base.kind_of? Integer) && 2 <= base && base <= 36 then
1013
- raise TypeError, "base must be integer between 2 and 36"
1014
- end
1015
- quot = (self.move_point_right(scale) * base ** shown_scale) / 10 ** scale
1016
- # p(quot)
1017
- rounded = quot.round_to_scale(0, mode)
1018
- # p(rounded)
1019
- rounded.to_s_internal(base, shown_scale)
840
+ def <<(other)
841
+ unless (other.kind_of? Fixnum) && other >= 0 then
842
+ raise TypeError, "cannot shift by something other than Fixnum >= 0"
1020
843
  end
844
+ LongDecimal(int_val << other, scale)
1021
845
  end
1022
846
 
1023
847
  #
1024
- # internal helper method, converts self to string in decimal system
1025
- # with default settings.
848
+ # performs bitwise right shift of self by other
1026
849
  #
1027
- def to_s_10
1028
- to_s_internal(10, scale)
850
+ def >>(other)
851
+ unless (other.kind_of? Fixnum) && other >= 0 then
852
+ raise TypeError, "cannot shift by something other than Fixnum >= 0"
853
+ end
854
+ LongDecimal(int_val >> other, scale)
1029
855
  end
1030
856
 
1031
857
  #
1032
- # internal helper method, converts self to string in any number system
858
+ # gets binary digit of self
1033
859
  #
1034
- def to_s_internal(b, sc)
1035
- sg = sgn
1036
- i = int_val.abs
1037
- str = i.to_s(b)
1038
- if sc > 0 then
1039
- missing = sc - str.length + 1
1040
- if missing > 0 then
1041
- str = ("0" * missing) + str
1042
- end
1043
- str[-sc, 0] = "."
1044
- end
1045
- str = "-" + str if sg < 0
1046
- str
860
+ def [](other)
861
+ int_val[other]
1047
862
  end
1048
863
 
1049
- protected :to_s_10
1050
- protected :to_s_internal
1051
-
1052
864
  #
1053
- # convert self into Rational
1054
- # this works quite straitforward. use int_val as numerator and a
1055
- # power of 10 as denominator
865
+ # gets size of int_val
1056
866
  #
1057
- def to_r
1058
- Rational(numerator, denominator)
867
+ def size
868
+ int_val.size
1059
869
  end
1060
870
 
1061
871
  #
1062
- # convert self into Float
1063
- # this works straitforward by dividing int_val by power of 10 in
1064
- # float-arithmetic, in all cases where numerator and denominator are
1065
- # within the ranges expressable as Floats. Goes via string
1066
- # representation otherwise.
872
+ # divide by 10**n
1067
873
  #
1068
- def to_f
1069
- divisor = denominator
1070
- if (divisor == 1) then
1071
- return numerator.to_f
1072
- elsif int_val.abs <= LongMath::MAX_FLOATABLE then
1073
- if (divisor.abs > LongMath::MAX_FLOATABLE) then
1074
- return 0.0
1075
- else
1076
- f = int_val.to_f
1077
- return f / divisor
1078
- end
1079
- elsif numerator.abs < divisor
1080
- # self is between -1 and 1
1081
- # factor = numerator.abs.div(LongMath::MAX_FLOATABLE)
1082
- # digits = factor.to_ld.int_digits10
1083
- # return LongDecimal(numerator.div(10**digits), scale -digits).to_f
1084
- return self.to_s.to_f
874
+ def move_point_left(n)
875
+ raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
876
+ if (n >= 0) then
877
+ move_point_left_int(n)
1085
878
  else
1086
- # s2 = [scale.div(2), 1].max
1087
- # return LongDecimal(numerator.div(10**s2), scale - s2).to_f
1088
- return self.to_s.to_f
879
+ move_point_right_int(-n)
1089
880
  end
1090
881
  end
1091
882
 
1092
883
  #
1093
- # convert self into Integer
1094
- # This may loose information. In most cases it is preferred to
1095
- # control this by calling round_to_scale first and then applying
1096
- # to_i when the number represented by self is actually an integer.
884
+ # multiply by 10**n
1097
885
  #
1098
- def to_i
1099
- numerator.div(denominator)
886
+ def move_point_right(n)
887
+ raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
888
+ if (n < 0) then
889
+ move_point_left_int(-n)
890
+ else
891
+ move_point_right_int(n)
892
+ end
1100
893
  end
1101
894
 
1102
895
  #
1103
- # convert self into LongDecimal (returns self)
896
+ # internal method
897
+ # divide by 10**n
1104
898
  #
1105
- def to_ld
1106
- self
899
+ def move_point_left_int(n)
900
+ raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
901
+ LongDecimal(int_val, scale + n)
1107
902
  end
1108
903
 
1109
904
  #
1110
- # convert selt into BigDecimal
905
+ # internal method
906
+ # multiply by 10**n
1111
907
  #
1112
- def to_bd
1113
- # this operation is probably not used so heavily, so we can live with a
1114
- # string as an intermediate step.
1115
- BigDecimal(self.to_s)
908
+ def move_point_right_int(n)
909
+ raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
910
+ if (n > scale) then
911
+ LongDecimal(int_val * 10**(n-scale), 0)
912
+ else
913
+ LongDecimal(int_val, scale-n)
914
+ end
1116
915
  end
1117
916
 
917
+ protected :move_point_left_int, :move_point_right_int
918
+
1118
919
  #
1119
- # LongDecimals can be seen as a fraction with a power of 10 as
1120
- # denominator for compatibility with other numeric classes this
1121
- # method is included, returning 10**scale.
1122
- # Please observe that there may be common factors of numerator and
1123
- # denominator in case of LongDecimal, which does not occur in case
1124
- # of Rational
920
+ # calculate the square of self
1125
921
  #
1126
- def denominator
1127
- 10**scale
922
+ def square
923
+ self * self
1128
924
  end
1129
925
 
1130
926
  #
1131
- # LongDecimals can be seen as a fraction with its int_val as its
1132
- # numerator
1133
- # Please observe that there may be common factors of numerator and
1134
- # denominator in case of LongDecimal, which does not occur in case
1135
- # of Rational
927
+ # calculate the sqrt of self
928
+ # provide the result with given number
929
+ # new_scale of digits after the decimal point
930
+ # use rounding_mode if the result is not exact
1136
931
  #
1137
- alias numerator int_val
932
+ def sqrt(new_scale, rounding_mode)
933
+ sqrt_internal(new_scale, rounding_mode, false)
934
+ end
1138
935
 
1139
936
  #
1140
- # number of binary digits before the decimal point, not counting a single 0.
1141
- # 0.xxx -> 0
1142
- # 1.xxx -> 1
1143
- # 2.xxx -> 2
1144
- # 4.xxx -> 3
1145
- # 8.xxx -> 4
1146
- # ...
937
+ # calculate the sqrt s of self and remainder r >= 0
938
+ # such that s*s+r = self and (s+1)*(s+1) > self
939
+ # provide the result with given number
940
+ # new_scale of digits after the decimal point
1147
941
  #
1148
- def int_digits2
1149
- int_part = self.abs.to_i
1150
- if int_part.zero? then
1151
- return 0
1152
- end
1153
-
1154
- n = int_part.size * 8 - 31
1155
- int_part = int_part >> n
1156
- until int_part.zero? do
1157
- int_part = int_part >> 1
1158
- n += 1
1159
- end
1160
- n
942
+ def sqrt_with_remainder(new_scale)
943
+ sqrt_internal(new_scale, ROUND_DOWN, true)
1161
944
  end
1162
945
 
946
+
1163
947
  #
1164
- # number of decimal digits before the decimal point, not counting a
1165
- # single 0.
1166
- # 0.xxx -> 0
1167
- # 1.xxx -> 1
1168
- # 10.xxx -> 2
1169
- # ...
948
+ # internal helper method for calculationg sqrt and sqrt_with_remainder
1170
949
  #
1171
- def int_digits10
1172
- int_part = self.abs.to_i
1173
- if int_part.zero? then
1174
- return 0
1175
- end
950
+ def sqrt_internal(new_scale, rounding_mode, with_rem)
951
+ raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
952
+ raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
953
+ raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless rounding_mode.kind_of? RoundingModeClass
1176
954
 
1177
- id = 1
1178
- powers = []
1179
- power = 10
1180
- idx = 0
1181
- until int_part.zero? do
1182
- expon = 1 << idx
1183
- powers[idx] = power
1184
- break if int_part < power
1185
- id += expon
1186
- int_part = (int_part / power).to_i
1187
- idx += 1
1188
- power = power * power
955
+ new_scale1 = new_scale
956
+ unless (with_rem) then
957
+ new_scale1 += 1
1189
958
  end
1190
- until int_part < 10 do
1191
- idx -= 1
1192
- expon = 1 << idx
1193
- power = powers[idx]
1194
- # puts("i=#{int_part} p=#{power}\n")
1195
- while int_part >= power
1196
- id += expon
1197
- int_part = (int_part / power).to_i
959
+ old_scale = (new_scale1 << 1)
960
+ x = round_to_scale(old_scale, rounding_mode)
961
+ root, rem = LongMath.sqrtw_with_remainder(x.int_val)
962
+ y = LongDecimal(root, new_scale1)
963
+ if (with_rem) then
964
+ r = LongDecimal(rem, old_scale)
965
+ return [ y, r ]
966
+ else
967
+ if ((rounding_mode == ROUND_HALF_EVEN || rounding_mode == ROUND_HALF_DOWN) && rem > 0) then
968
+ rounding_mode = ROUND_HALF_UP
1198
969
  end
970
+ y = y.round_to_scale(new_scale, rounding_mode)
971
+ return y
1199
972
  end
1200
- id
1201
973
  end
1202
974
 
975
+ private :sqrt_internal
976
+
1203
977
  #
1204
- # before adding or subtracting two LongDecimal numbers
1205
- # it is mandatory to set them to the same scale. The maximum of the
1206
- # two summands is used, in order to avoid loosing any information.
1207
- # this method is mostly for internal use
978
+ # calculate the multiplicative inverse
1208
979
  #
1209
- def equalize_scale(other)
1210
- o, s = coerce(other)
1211
- if (s.kind_of? LongDecimal) then
1212
- # make sure Floats do not mess up our number of significant digits when adding
1213
- if (other.kind_of? Float) then
1214
- o = o.round_to_scale(s.scale, ROUND_HALF_UP)
1215
- else
1216
- new_scale = [s.scale, o.scale].max
1217
- s = s.round_to_scale(new_scale)
1218
- o = o.round_to_scale(new_scale)
1219
- end
1220
- end
1221
- return s, o
980
+ def reciprocal
981
+ 1 / self
1222
982
  end
1223
983
 
984
+ alias inverse reciprocal
985
+
1224
986
  #
1225
- # before dividing two LongDecimal numbers, it is mandatory to set
1226
- # make them both to integers, so the result is simply expressable as
1227
- # a rational
1228
- # this method is mostly for internal use
987
+ # Absolute value
1229
988
  #
1230
- def anti_equalize_scale(other)
1231
- o, s = coerce(other)
1232
- if (s.kind_of? LongDecimal) then
1233
- exponent = [s.scale, o.scale].max
1234
- factor = 10**exponent
1235
- s *= factor
1236
- o *= factor
1237
- s = s.round_to_scale(0)
1238
- o = o.round_to_scale(0)
1239
- end
1240
- return s, o
989
+ def abs
990
+ LongDecimal(int_val.abs, scale)
1241
991
  end
1242
992
 
1243
993
  #
1244
- # successor as needed for using LongDecimal in ranges
1245
- # it needs to be observed that this is usually not an increment by
1246
- # 1, but by 1/10**scale.
994
+ # square of absolute value
995
+ # happens to be the square
1247
996
  #
1248
- def succ
1249
- LongDecimal(int_val + 1, scale)
1250
- end
1251
-
1252
- alias next succ
997
+ alias abs2 square
1253
998
 
1254
999
  #
1255
- # predecessor (opposite of successor)
1256
- # it needs to be observed that this is usually not an decrement by
1257
- # 1, but by 1/10**scale.
1000
+ # Compares the two numbers.
1001
+ # returns -1 if self < other
1002
+ # 0 if self-other = 0
1003
+ # +1 if self > other
1004
+ # it needs to be observed, that
1005
+ # x == y implies (x <=> y) == 0
1006
+ # but not
1007
+ # (x <=> y) == 0 implies x == y
1008
+ # because == also takes the scale into account and considers two
1009
+ # numbers only equal, if they have the same number of potentially
1010
+ # zero digits after the decimal point.
1258
1011
  #
1259
- def pred
1260
- LongDecimal(int_val - 1, scale)
1012
+ def <=> (other)
1013
+ diff = (self - other)
1014
+ if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
1015
+ diff.sgn
1016
+ else
1017
+ diff <=> 0
1018
+ end
1261
1019
  end
1262
1020
 
1263
1021
  #
1264
- # self + 1
1022
+ # <=>-comparison for the scales
1265
1023
  #
1266
- def inc
1267
- self + 1
1024
+ def scale_ufo(other)
1025
+ raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
1026
+ self.scale <=> other.scale
1268
1027
  end
1269
1028
 
1270
1029
  #
1271
- # self - 1
1030
+ # ==-comparison for the scales
1272
1031
  #
1273
- def dec
1274
- self - 1
1032
+ def scale_equal(other)
1033
+ scale_ufo(other).zero?
1275
1034
  end
1276
1035
 
1277
1036
  #
1278
- # self += 1
1037
+ # return a pair o, s resembling other, self, but potentially
1038
+ # converted to compatible types and ready for
1039
+ # arithmetic operations.
1279
1040
  #
1280
- def inc!
1281
- @int_val += denominator
1041
+ def coerce(other)
1042
+ if other.kind_of? LongDecimal then
1043
+ return other, self
1044
+ elsif other.kind_of? LongDecimalQuot then
1045
+ return other, LongDecimalQuot(self.to_r, scale)
1046
+ elsif other.kind_of? Rational then
1047
+ sc = scale
1048
+ o = LongDecimalQuot(other, sc)
1049
+ s = LongDecimalQuot(self.to_r, sc)
1050
+ return o, s
1051
+ elsif (other.kind_of? Integer) || (other.kind_of? Float) then
1052
+ other = LongDecimal(other)
1053
+ if (other.scale > scale) then
1054
+ other = other.round_to_scale(scale, ROUND_HALF_UP)
1055
+ end
1056
+ return other, self
1057
+ elsif other.kind_of? BigDecimal then
1058
+ s, o = other.coerce(self.to_bd)
1059
+ return o, s
1060
+ elsif other.kind_of? Complex then
1061
+ # s, o = other.coerce(Complex(self.to_bd, 0))
1062
+ s, o = other.coerce(Complex(self.to_f, 0))
1063
+ return o, s
1064
+ elsif (other.kind_of? Float) && size > 8 then
1065
+ return coerce(BigDecimal(other.to_s))
1066
+ elsif other.kind_of? Numeric then
1067
+ s, o = other.coerce(self.to_f)
1068
+ return o, s
1069
+ else
1070
+ raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimal"
1071
+ end
1282
1072
  end
1283
1073
 
1284
1074
  #
1285
- # self -= 1
1075
+ # is self expressable as an integer without loss of digits?
1286
1076
  #
1287
- def dec!
1288
- @int_val -= denominator
1077
+ def is_int?
1078
+ scale == 0 || int_val % 10**scale == 0
1289
1079
  end
1290
1080
 
1291
1081
  #
1292
- # return the unit by which self is incremented by succ
1082
+ # get the sign of self
1083
+ # -1 if self < 0
1084
+ # 0 if self is 0 (with any number of 0s after the decimal point)
1085
+ # +1 if self > 0
1293
1086
  #
1294
- def unit
1295
- LongDecimal(1, scale)
1087
+ def sgn
1088
+ int_val <=> 0
1296
1089
  end
1297
1090
 
1091
+ alias signum sgn
1092
+ alias sign sgn
1093
+
1298
1094
  #
1299
- # apply unary +
1300
- # (returns self)
1095
+ # comparison of self with other for equality
1096
+ # takes into account the values expressed by self and other and the
1097
+ # equality of the number of digits.
1301
1098
  #
1302
- def +@
1303
- self
1099
+ def ==(other)
1100
+ # (other.kind_of? LongDecimal) && (self <=> other) == 0 && self.scale == other.scale
1101
+ (other.kind_of? LongDecimal) && self.int_val == other.int_val && self.scale == other.scale
1304
1102
  end
1305
1103
 
1306
1104
  #
1307
- # apply unary -
1308
- # (returns negated self)
1105
+ # check if the number expressed by self is 0 (zero)
1106
+ # with any number of 0s after the decimal point.
1309
1107
  #
1310
- def -@
1311
- if self.zero? then
1312
- self
1313
- else
1314
- LongDecimal(-int_val, scale)
1315
- end
1108
+ def zero?
1109
+ int_val.zero?
1316
1110
  end
1317
1111
 
1318
1112
  #
1319
- # add two numbers
1320
- # if both can immediately be expressed as LongDecimal, the result is
1321
- # a LongDecimal as well. The number of digits after the decimal
1322
- # point is the max of the scales of the summands
1323
- # if LongDecimal does not cover the two summands, call addition of
1324
- # Complex, Float or LongRationalQuot
1113
+ # check if the number expressed by self is 1 (one)
1114
+ # with any number of 0s after the decimal point.
1325
1115
  #
1326
- def +(other)
1327
- s, o = equalize_scale(other)
1328
- if s.kind_of? LongDecimal then
1329
- LongDecimal(s.int_val + o.int_val, s.scale)
1330
- else
1331
- s + o
1332
- end
1116
+ def one?
1117
+ (self-1).zero?
1333
1118
  end
1334
1119
 
1335
1120
  #
1336
- # subtract two numbers
1337
- # if both can immediately be expressed as LongDecimal, the result is
1338
- # a LongDecimal as well. The number of digits after the decimal
1339
- # point is the max of the scales of self and other.
1340
- # if LongDecimal does not cover self and other, the subtraction of
1341
- # Complex, Float or LongRationalQuot is used
1121
+ # Returns a hash code for the complex number.
1342
1122
  #
1343
- def -(other)
1344
- s, o = equalize_scale(other)
1345
- if s.kind_of? LongDecimal then
1346
- LongDecimal(s.int_val - o.int_val, s.scale)
1347
- else
1348
- s - o
1349
- end
1123
+ def hash
1124
+ int_val.hash ^ scale.hash
1350
1125
  end
1351
1126
 
1352
1127
  #
1353
- # multiply two numbers
1354
- # if both can immediately be expressed as LongDecimal, the result is
1355
- # a LongDecimal as well. The number of digits after the decimal
1356
- # point is the sum of the scales of both factors.
1357
- # if LongDecimal does not cover self and other, the multiplication of
1358
- # Complex, Float or LongRationalQuot is used
1128
+ # Returns "<tt>LongDecimal(<i>int_val</i>, <i>scale</i>)</tt>".
1359
1129
  #
1360
- def *(other)
1361
- o, s = coerce(other)
1362
- if s.kind_of? LongDecimal then
1363
- LongDecimal(s.int_val * o.int_val, s.scale + o.scale)
1364
- else
1365
- s * o
1366
- end
1130
+ def inspect
1131
+ sprintf("LongDecimal(%s, %s)", int_val.inspect, scale.inspect)
1367
1132
  end
1368
1133
 
1134
+ end # LongDecimal
1135
+
1136
+ #
1137
+ # This class is used for storing intermediate results after having
1138
+ # performed a division. The division cannot be completed without
1139
+ # providing additional information on how to round the result.
1140
+ #
1141
+ class LongDecimalQuot < Numeric
1142
+
1143
+ @RCS_ID='-$Id: long-decimal.rb,v 1.6 2006/03/20 21:38:32 bk1 Exp $-'
1144
+
1145
+ include LongDecimalRoundingMode
1146
+
1369
1147
  #
1370
- # divide self by other and round result to scale of self using the
1371
- # given rounding mode
1148
+ # constructor
1149
+ # first, second is either a pair of LongDecimals or a Rational and an Integer
1150
+ # The resulting LongDecimal will contain a rational obtained by
1151
+ # dividing the two LongDecimals or by taking the Rational as it is.
1152
+ # The scale is there to provide a default rounding precision for
1153
+ # conversion to LongDecimal, but it has no influence on the value
1154
+ # expressed by the LongDecimalQuot
1372
1155
  #
1373
- def divide(other, rounding_mode)
1374
- divide_s(other, nil, rounding_mode)
1156
+ def LongDecimalQuot.new!(first, second)
1157
+ new(first, second)
1375
1158
  end
1376
1159
 
1377
1160
  #
1378
- # divide self by other and round result to new_scale using the
1379
- # given rounding mode. If new_scale is nil, use scale of self.
1161
+ # create a new LongDecimalQuot from a rational and a scale or a
1162
+ # pair of LongDecimals
1380
1163
  #
1381
- def divide_s(other, new_scale, rounding_mode)
1382
- q = self / other
1383
- if (q.kind_of? Float) then
1384
- q = LongDecimal(q)
1385
- end
1386
- if (q.kind_of? LongDecimal) || (q.kind_of? LongDecimalQuot) then
1387
- if (new_scale.nil?) then
1388
- new_scale = q.scale
1389
- end
1390
- q.round_to_scale(new_scale, rounding_mode)
1164
+ def initialize(first, second)
1165
+ if ((first.kind_of? Rational) || (first.kind_of? Integer)) && (second.kind_of? Integer) then
1166
+ @rat = Rational(first.numerator, first.denominator)
1167
+ @scale = second
1168
+ elsif (first.kind_of? LongDecimal) && (second.kind_of? LongDecimal) then
1169
+ orig_scale = first.scale
1170
+ first, second = first.anti_equalize_scale(second)
1171
+ @rat = Rational(first.to_i, second.to_i)
1172
+ @scale = orig_scale
1391
1173
  else
1392
- q
1174
+ raise TypeError, "parameters must be (LongDecimal, LongDecimal) or (Rational, Integer): first=#{first.inspect} second=#{second.inspect}";
1393
1175
  end
1394
1176
  end
1395
1177
 
1178
+ attr_reader :scale, :rat
1179
+
1396
1180
  #
1397
- # divide self by other and return result as Rational, if other
1398
- # allowed exact calculations.
1181
+ # numerator of the included rational number.
1182
+ # LongDecimals should duck type like Rationals
1399
1183
  #
1400
- def rdiv(other)
1401
- q = self / other
1402
- if (q.kind_of? LongDecimalQuot) then
1403
- q.to_r
1404
- else
1405
- q
1406
- end
1184
+ def numerator
1185
+ rat.numerator
1407
1186
  end
1408
1187
 
1409
1188
  #
1410
- # divide self by other and return result as LongDecimalQuot
1411
- # because division does not have an obvious rounding rule like
1412
- # addition, subtraction and multiplication, the result needs to be
1413
- # rounded afterwards to become a LongDecimal again. This way
1414
- # calculations can still be done in the natural readable way using +,
1415
- # -, *, and /, but the rounding can be provided later.
1416
- # It is very important in complicated calculations put the rounding
1417
- # steps in the right places, usually after having performed a division.
1189
+ # denominator of the included rational number.
1190
+ # LongDecimals should duck type like Rationals
1418
1191
  #
1419
- def /(other)
1420
- o, s = coerce(other)
1421
- if (s.kind_of? LongDecimal) then
1422
- LongDecimalQuot(s, o)
1423
- else
1424
- s / o
1425
- end
1192
+ def denominator
1193
+ rat.denominator
1426
1194
  end
1427
1195
 
1428
1196
  #
1429
- # power of self (LongDecimal) with other.
1430
- # if other is expressable as non-negative integer, the power is what
1431
- # would be obtained by successive multiplications.
1432
- # if other is expressable as negative integer, the power is a
1433
- # LongDecimalQuot as would result by successive division, but with
1434
- # the same scale as the positive power would get. Explicit rounding
1435
- # is needed to convert into a LongDecimal again
1436
- # in all other cases, self is converted into a Rational prior to
1437
- # applying power, usually resulting in a Float as power.
1197
+ # alter scale (only for internal use)
1438
1198
  #
1439
- def **(other)
1440
- if ((other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)) && other.is_int? then
1441
- other = other.to_i
1442
- end
1443
- if other.kind_of? Integer then
1444
- if other >= 0 then
1445
- LongDecimal(int_val ** other, scale * other)
1446
- else
1447
- abs_other = -other
1448
- new_scale = abs_other * scale
1449
- LongDecimalQuot(Rational(10 ** new_scale, int_val ** abs_other), new_scale)
1450
- end
1451
- else
1452
- if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
1453
- other = other.to_r
1454
- end
1455
- self.to_r ** other
1456
- end
1199
+ def scale=(s)
1200
+ raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
1201
+ raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
1202
+ @scale = s
1457
1203
  end
1458
1204
 
1205
+ private :scale=
1206
+
1459
1207
  #
1460
- # do integer division with remainder, returning two values
1208
+ # conversion to string. Based on the conversion of Rational
1461
1209
  #
1462
- def divmod(other)
1463
- if (other.kind_of? Complex) then
1464
- raise TypeError, "divmod not supported for Complex"
1465
- end
1466
- q = (self / other).to_i
1467
- return q, self - other * q
1210
+ def to_s
1211
+ str = @rat.to_s
1212
+ str + "[" + scale.to_s + "]"
1468
1213
  end
1469
1214
 
1470
1215
  #
1471
- # remainder of integer division by other
1216
+ # conversion to rational
1472
1217
  #
1473
- def %(other)
1474
- q, r = divmod other
1475
- r
1218
+ def to_r
1219
+ Rational(numerator, denominator)
1476
1220
  end
1477
1221
 
1478
1222
  #
1479
- # performs bitwise AND between self and other
1223
+ # convert into Float
1480
1224
  #
1481
- def &(other)
1482
- s, o = equalize_scale(other)
1483
- if s.kind_of? LongDecimal then
1484
- LongDecimal(s.int_val & o.int_val, s.scale)
1485
- else
1486
- s & o
1487
- end
1225
+ def to_f
1226
+ to_r.to_f
1488
1227
  end
1489
1228
 
1490
1229
  #
1491
- # performs bitwise OR between self and other
1230
+ # convert into Integer
1492
1231
  #
1493
- def |(other)
1494
- s, o = equalize_scale(other)
1495
- if s.kind_of? LongDecimal then
1496
- LongDecimal(s.int_val | o.int_val, s.scale)
1497
- else
1498
- s | o
1499
- end
1232
+ def to_i
1233
+ to_r.to_i
1500
1234
  end
1501
1235
 
1502
1236
  #
1503
- # performs bitwise XOR between self and other
1237
+ # conversion to LongDecimal using the internal scale
1504
1238
  #
1505
- def ^(other)
1506
- s, o = equalize_scale(other)
1507
- if s.kind_of? LongDecimal then
1508
- LongDecimal(s.int_val ^ o.int_val, s.scale)
1509
- else
1510
- s ^ o
1511
- end
1239
+ def to_ld
1240
+ round_to_scale(scale, ROUND_HALF_UP)
1512
1241
  end
1513
1242
 
1514
1243
  #
1515
- # bitwise inversion
1244
+ # unary plus returns self
1516
1245
  #
1517
- def ~
1518
- LongDecimal(~int_val, scale)
1246
+ def +@
1247
+ self
1519
1248
  end
1520
1249
 
1521
1250
  #
1522
- # performs bitwise left shift of self by other
1251
+ # unary minus returns negation of self
1252
+ # leaves self unchanged.
1523
1253
  #
1524
- def <<(other)
1525
- unless (other.kind_of? Integer) && other >= 0 then
1526
- raise TypeError, "cannot shift by something other than integer >= 0"
1254
+ def -@
1255
+ if self.zero? then
1256
+ self
1257
+ else
1258
+ LongDecimalQuot(-rat, scale)
1527
1259
  end
1528
- LongDecimal(s.int_val << other, s.scale)
1529
1260
  end
1530
1261
 
1531
1262
  #
1532
- # performs bitwise right shift of self by other
1263
+ # addition
1264
+ # if other can be converted into LongDecimalQuot, add as
1265
+ # LongDecimalQuot, using the addition of Rationals internally
1266
+ # otherwise use BigDecimal, Complex or Float
1533
1267
  #
1534
- def >>(other)
1535
- unless (other.kind_of? Integer) && other >= 0 then
1536
- raise TypeError, "cannot shift by something other than integer >= 0"
1268
+ def +(other)
1269
+ o, s = coerce(other)
1270
+ if (s.kind_of? LongDecimalQuot) then
1271
+ LongDecimalQuot(s.rat + o.rat, [s.scale, o.scale].max)
1272
+ else
1273
+ s + o
1537
1274
  end
1538
- LongDecimal(s.int_val >> other, s.scale)
1539
1275
  end
1540
1276
 
1541
1277
  #
1542
- # gets binary digit of self
1278
+ # subtraction
1279
+ # if other can be converted into LongDecimalQuot, add as
1280
+ # LongDecimalQuot, using the subtraction of Rationals internally
1281
+ # otherwise use BigDecimal, Complex or Float
1543
1282
  #
1544
- def [](other)
1545
- int_val[other]
1283
+ def -(other)
1284
+ o, s = coerce(other)
1285
+ if (s.kind_of? LongDecimalQuot) then
1286
+ LongDecimalQuot(s.rat - o.rat, [s.scale, o.scale].max)
1287
+ else
1288
+ s - o
1289
+ end
1546
1290
  end
1547
1291
 
1548
1292
  #
1549
- # gets size of int_val
1293
+ # multiplication
1294
+ # if other can be converted into LongDecimalQuot, add as
1295
+ # LongDecimalQuot, using the multiplication of Rationals internally
1296
+ # otherwise use BigDecimal, Complex or Float
1550
1297
  #
1551
- def size
1552
- int_val.size
1298
+ def *(other)
1299
+ o, s = coerce(other)
1300
+ if (s.kind_of? LongDecimalQuot) then
1301
+ LongDecimalQuot(s.rat * o.rat, s.scale + o.scale)
1302
+ else
1303
+ s * o
1304
+ end
1553
1305
  end
1554
1306
 
1555
1307
  #
1556
- # divide by 10**n
1308
+ # division
1309
+ # if other can be converted into LongDecimalQuot, add as
1310
+ # LongDecimalQuot, using the division of Rationals internally
1311
+ # otherwise use BigDecimal, Complex or Float
1557
1312
  #
1558
- def move_point_left(n)
1559
- raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
1560
- if (n >= 0) then
1561
- move_point_left_int(n)
1313
+ def /(other)
1314
+ o, s = coerce(other)
1315
+ if (s.kind_of? LongDecimalQuot) then
1316
+ LongDecimalQuot(s.rat / o.rat, scale)
1562
1317
  else
1563
- move_point_right_int(-n)
1318
+ s / o
1564
1319
  end
1565
1320
  end
1566
1321
 
1567
1322
  #
1568
- # multiply by 10**n
1323
+ # potentiation
1324
+ # if other can be converted into integer, use power of rational base
1325
+ # with integral exponent internally
1326
+ # otherwise result will be Float, BigDecimal or Complex
1569
1327
  #
1570
- def move_point_right(n)
1571
- raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
1572
- if (n < 0) then
1573
- move_point_left_int(-n)
1328
+ def **(other)
1329
+ if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
1330
+ if other.is_int? then
1331
+ other = other.to_i
1332
+ else
1333
+ other = other.to_r
1334
+ end
1335
+ end
1336
+ rat_result = rat ** other
1337
+ if (rat_result.kind_of? Rational) then
1338
+ if (other.kind_of? Integer) && other >= 0 then
1339
+ new_scale = scale * other
1340
+ else
1341
+ new_scale = scale
1342
+ end
1343
+ LongDecimalQuot(rat_result, new_scale)
1574
1344
  else
1575
- move_point_right_int(n)
1345
+ rat_result
1576
1346
  end
1577
1347
  end
1578
1348
 
1579
1349
  #
1580
- # internal method
1581
- # divide by 10**n
1350
+ # division with remainder
1351
+ # calculate q and r such that
1352
+ # q is an integer and r is non-negative and less or equal the
1353
+ # divisor.
1582
1354
  #
1583
- def move_point_left_int(n)
1584
- raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
1585
- LongDecimal(int_val, scale + n)
1355
+ def divmod(other)
1356
+ if (other.kind_of? Complex) then
1357
+ raise TypeError, "divmod not supported for Complex"
1358
+ end
1359
+ q = (self / other).to_i
1360
+ return q, self - other * q
1586
1361
  end
1587
-
1362
+
1588
1363
  #
1589
- # internal method
1590
- # multiply by 10**n
1364
+ # division with remainder
1365
+ # only return the remainder
1591
1366
  #
1592
- def move_point_right_int(n)
1593
- raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
1594
- if (n > scale) then
1595
- LongDecimal(int_val * 10**(n-scale), 0)
1596
- else
1597
- LongDecimal(int_val, scale-n)
1598
- end
1367
+ def %(other)
1368
+ q, r = divmod other
1369
+ r
1599
1370
  end
1600
1371
 
1601
- protected :move_point_left_int, :move_point_right_int
1602
-
1603
1372
  #
1604
1373
  # calculate the square of self
1605
1374
  #
@@ -1607,57 +1376,141 @@ class LongDecimal < Numeric
1607
1376
  self * self
1608
1377
  end
1609
1378
 
1610
- def sqrt(new_scale, rounding_mode)
1611
- raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
1612
- raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
1613
- raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless rounding_mode.kind_of? RoundingModeClass
1379
+ #
1380
+ # calculate the multiplicative inverse
1381
+ #
1382
+ def reciprocal
1383
+ 1 / self
1384
+ end
1614
1385
 
1615
- new_scale1 = new_scale + 1
1616
- old_scale = (new_scale1 << 1)
1617
- x = round_to_scale(old_scale, rounding_mode)
1618
- root, rem = LongMath.sqrtb_with_remainder(x.int_val)
1619
- if (rem > 0 && (rounding_mode == ROUND_HALF_EVEN || rounding_mode == ROUND_HALF_DOWN)) then
1620
- rounding_mode = ROUND_HALF_UP
1621
- end
1622
- y = LongDecimal(root, new_scale1)
1623
- y.round_to_scale(new_scale, rounding_mode)
1386
+ #
1387
+ # Absolute value
1388
+ #
1389
+ def abs
1390
+ LongDecimalQuot(rat.abs, scale)
1624
1391
  end
1625
1392
 
1393
+ #
1394
+ # square of absolute value
1395
+ #
1396
+ def abs2
1397
+ self.abs.square
1398
+ end
1626
1399
 
1627
1400
  #
1628
- # calculate the multiplicative inverse
1401
+ # convert LongDecimalQuot to LongDecimal with the given precision
1402
+ # and the given rounding mode
1629
1403
  #
1630
- def reciprocal
1631
- 1 / self
1404
+ def round_to_scale(new_scale = @scale, mode = ROUND_UNNECESSARY)
1405
+
1406
+ raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
1407
+ raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
1408
+ raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
1409
+
1410
+ factor = 10**new_scale
1411
+ sign_quot = numerator <=> 0
1412
+ if sign_quot == 0 then
1413
+ return LongDecimal(0, new_scale)
1414
+ end
1415
+ prod = numerator * factor
1416
+ divisor = denominator
1417
+ quot, rem = prod.divmod(divisor)
1418
+ sign_rem = rem <=> 0
1419
+ if (sign_rem == 0)
1420
+ return LongDecimal(quot, new_scale)
1421
+ end
1422
+ raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem <= 0
1423
+ if (sign_quot < 0) then
1424
+ rem -= divisor
1425
+ quot += 1
1426
+ sign_rem = rem <=> 0
1427
+ raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem >= 0
1428
+ end
1429
+
1430
+ if mode == ROUND_UNNECESSARY then
1431
+ raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
1432
+ end
1433
+
1434
+ if (mode == ROUND_CEILING)
1435
+ mode = (sign_quot > 0) ? ROUND_UP : ROUND_DOWN
1436
+ elsif (mode == ROUND_FLOOR)
1437
+ mode = (sign_quot < 0) ? ROUND_UP : ROUND_DOWN
1438
+ else
1439
+ abs_rem = rem.abs
1440
+ half = (abs_rem << 1) <=> denominator
1441
+ if (mode == ROUND_HALF_UP || mode == ROUND_HALF_DOWN || mode == ROUND_HALF_EVEN) then
1442
+ if (half < 0) then
1443
+ mode = ROUND_DOWN
1444
+ elsif half > 0 then
1445
+ mode = ROUND_UP
1446
+ else
1447
+ # half == 0
1448
+ if (mode == ROUND_HALF_UP) then
1449
+ mode = ROUND_UP
1450
+ elsif (mode == ROUND_HALF_DOWN) then
1451
+ mode = ROUND_DOWN
1452
+ else
1453
+ # mode == ROUND_HALF_EVEN
1454
+ mode = (quot[0] == 1 ? ROUND_UP : ROUND_DOWN)
1455
+ end
1456
+ end
1457
+ end
1458
+ end
1459
+
1460
+ if mode == ROUND_UP
1461
+ quot += sign_quot
1462
+ end
1463
+ new_int_val = quot
1464
+ LongDecimal(new_int_val, new_scale)
1632
1465
  end
1633
1466
 
1634
- alias inverse reciprocal
1635
-
1636
1467
  #
1637
- # Absolute value
1468
+ # prepare binary operation of other with LongDecimalQuot
1469
+ # Integer, LongDecimal, Rational and LongDecimalQuot can be
1470
+ # expressed as LongDecimalQuot, using the scale of self in case of
1471
+ # Integer and Rational. Floats can be approximated by LongDecimals
1472
+ # and thus be expressed as LongDecimalQuot
1473
+ # In case of BigDecimal, Complex or any unknown type, convert self
1474
+ # to BigDecimal or Float.
1638
1475
  #
1639
- def abs
1640
- LongDecimal(int_val.abs, scale)
1476
+ def coerce(other)
1477
+ if other.kind_of? LongDecimal then
1478
+ return LongDecimalQuot(other.to_r, other.scale), self
1479
+ elsif other.kind_of? LongDecimalQuot then
1480
+ return other, self
1481
+ elsif other.kind_of? Rational then
1482
+ s = scale
1483
+ return LongDecimalQuot(other, s), self
1484
+ elsif (other.kind_of? Integer) then
1485
+ return LongDecimalQuot(other.to_r, scale), self
1486
+ elsif other.kind_of? Float then
1487
+ return LongDecimalQuot(other.to_ld.to_r, scale), self
1488
+ elsif other.kind_of? BigDecimal then
1489
+ s, o = other.coerce(self.to_bd)
1490
+ elsif other.kind_of? Numeric then
1491
+ s, o = other.coerce(self.to_f)
1492
+ return o, s
1493
+ else
1494
+ raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimalQuot"
1495
+ end
1641
1496
  end
1642
1497
 
1643
1498
  #
1644
- # square of absolute value
1645
- # happens to be the square
1499
+ # compare two numbers for equality.
1500
+ # The LongDecimalQuot self is considered == to other if and only if
1501
+ # other is also LongDecimalQuot, expresses the same value and has the
1502
+ # same scale.
1503
+ # It needs to be observed that scale does not influence the value expressed
1504
+ # by the number, but only how rouding is performed by default if no
1505
+ # explicit number of digits after the decimal point is given. But
1506
+ # scale needs to match for equality.
1646
1507
  #
1647
- alias abs2 square
1508
+ def ==(other)
1509
+ (other.kind_of? LongDecimalQuot) && (self <=> other) == 0 && self.scale == other.scale
1510
+ end
1648
1511
 
1649
1512
  #
1650
- # Compares the two numbers.
1651
- # returns -1 if self < other
1652
- # 0 if self-other = 0
1653
- # +1 if self > other
1654
- # it needs to be observed, that
1655
- # x == y implies (x <=> y) == 0
1656
- # but not
1657
- # (x <=> y) == 0 implies x == y
1658
- # because == also takes the scale into account and considers two
1659
- # numbers only equal, if they have the same number of potentially
1660
- # zero digits after the decimal point.
1513
+ # Compares the two numbers for < and > etc.
1661
1514
  #
1662
1515
  def <=> (other)
1663
1516
  diff = (self - other)
@@ -1669,7 +1522,7 @@ class LongDecimal < Numeric
1669
1522
  end
1670
1523
 
1671
1524
  #
1672
- # <=>-comparison for the scales
1525
+ # compare scales with <=>
1673
1526
  #
1674
1527
  def scale_ufo(other)
1675
1528
  raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
@@ -1677,555 +1530,759 @@ class LongDecimal < Numeric
1677
1530
  end
1678
1531
 
1679
1532
  #
1680
- # ==-comparison for the scales
1533
+ # check if scales are equal
1681
1534
  #
1682
1535
  def scale_equal(other)
1683
1536
  scale_ufo(other).zero?
1684
1537
  end
1685
1538
 
1686
- #
1687
- # return a pair o, s resembling other, self, but potentially
1688
- # converted to compatible types and ready for
1689
- # arithmetic operations.
1690
- #
1691
- def coerce(other)
1692
- if other.kind_of? LongDecimal then
1693
- return other, self
1694
- elsif other.kind_of? LongDecimalQuot then
1695
- return other, LongDecimalQuot(self.to_r, scale)
1696
- elsif other.kind_of? Rational then
1697
- sc = scale
1698
- o = LongDecimalQuot(other, sc)
1699
- s = LongDecimalQuot(self.to_r, sc)
1700
- return o, s
1701
- elsif (other.kind_of? Integer) || (other.kind_of? Float) then
1702
- other = LongDecimal(other)
1703
- if (other.scale > scale) then
1704
- other = other.round_to_scale(scale, ROUND_HALF_UP)
1705
- end
1706
- return other, self
1707
- elsif other.kind_of? BigDecimal then
1708
- s, o = other.coerce(self.to_bd)
1709
- return o, s
1710
- elsif other.kind_of? Complex then
1711
- # s, o = other.coerce(Complex(self.to_bd, 0))
1712
- s, o = other.coerce(Complex(self.to_f, 0))
1713
- return o, s
1714
- elsif (other.kind_of? Float) && size > 8 then
1715
- return coerce(BigDecimal(other.to_s))
1716
- elsif other.kind_of? Numeric then
1717
- s, o = other.coerce(self.to_f)
1718
- return o, s
1719
- else
1720
- raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimal"
1721
- end
1722
- end
1723
-
1724
1539
  #
1725
1540
  # is self expressable as an integer without loss of digits?
1726
1541
  #
1727
1542
  def is_int?
1728
- scale == 0 || int_val % 10**scale == 0
1543
+ denominator == 1
1729
1544
  end
1730
1545
 
1731
1546
  #
1732
- # get the sign of self
1733
- # -1 if self < 0
1734
- # 0 if self is 0 (with any number of 0s after the decimal point)
1735
- # +1 if self > 0
1547
+ # sign of self
1736
1548
  #
1737
1549
  def sgn
1738
- int_val <=> 0
1550
+ numerator <=> 0
1739
1551
  end
1740
-
1741
1552
  alias signum sgn
1742
1553
  alias sign sgn
1743
1554
 
1744
- #
1745
- # comparison of self with other for equality
1746
- # takes into account the values expressed by self and other and the
1747
- # equality of the number of digits.
1748
- #
1749
- def ==(other)
1750
- # (other.kind_of? LongDecimal) && (self <=> other) == 0 && self.scale == other.scale
1751
- (other.kind_of? LongDecimal) && self.int_val == other.int_val && self.scale == other.scale
1752
- end
1753
-
1754
- #
1755
- # check if the number expressed by self is 0 (zero)
1756
- # with any number of 0s after the decimal point.
1757
- #
1758
- def zero?
1759
- int_val.zero?
1760
- end
1761
-
1762
- #
1763
- # check if the number expressed by self is 1 (one)
1764
- # with any number of 0s after the decimal point.
1765
- #
1766
- def one?
1767
- (self-1).zero?
1768
- end
1769
-
1770
1555
  #
1771
1556
  # Returns a hash code for the complex number.
1772
1557
  #
1773
1558
  def hash
1774
- int_val.hash ^ scale.hash
1559
+ rat.hash ^ scale.hash
1775
1560
  end
1776
1561
 
1562
+
1777
1563
  #
1778
- # Returns "<tt>LongDecimal(<i>int_val</i>, <i>scale</i>)</tt>".
1564
+ # Returns "<tt>LongDecimalQuot(<i>int_val</i>, <i>scale</i>, <i>num</i>, <i>denom</i>)</tt>".
1779
1565
  #
1780
1566
  def inspect
1781
- sprintf("LongDecimal(%s, %s)", int_val.inspect, scale.inspect)
1567
+ sprintf("LongDecimalQuot(Rational(%s, %s), %s)", numerator.inspect, denominator.inspect, scale.inspect)
1782
1568
  end
1783
1569
 
1784
- end # LongDecimal
1570
+ end # LongDecimalQuot
1785
1571
 
1786
1572
  #
1787
- # This class is used for storing intermediate results after having
1788
- # performed a division. The division cannot be completed without
1789
- # providing additional information on how to round the result.
1573
+ # Creates a LongDecimal number. +a+ and +b+ should be Numeric.
1790
1574
  #
1791
- class LongDecimalQuot < Numeric
1575
+ def LongDecimal(a, b = 0)
1576
+ if b == 0 && (a.kind_of? LongDecimal) then
1577
+ a
1578
+ else
1579
+ LongDecimal.new!(a, b)
1580
+ end
1581
+ end
1792
1582
 
1793
- @RCS_ID='-$Id: long-decimal.rb,v 1.2 2006/03/19 11:17:55 bk1 Exp $-'
1583
+ #
1584
+ # construct a LongDecimalQuot from the given parameters
1585
+ # 1st case: both are LongDecimals
1586
+ # 2nd case: first is Rational, second is scale
1587
+ #
1588
+ def LongDecimalQuot(first, second)
1589
+ LongDecimalQuot.new!(first, second)
1590
+ end
1794
1591
 
1795
- include LongDecimalRoundingMode
1796
1592
 
1797
- #
1798
- # constructor
1799
- # first, second is either a pair of LongDecimals or a Rational and an Integer
1800
- # The resulting LongDecimal will contain a rational obtained by
1801
- # dividing the two LongDecimals or by taking the Rational as it is.
1802
- # The scale is there to provide a default rounding precision for
1803
- # conversion to LongDecimal, but it has no influence on the value
1804
- # expressed by the LongDecimalQuot
1805
- #
1806
- def LongDecimalQuot.new!(first, second)
1807
- new(first, second)
1808
- end
1593
+ class Numeric
1809
1594
 
1810
1595
  #
1811
- # create a new LongDecimalQuot from a rational and a scale or a
1812
- # pair of LongDecimals
1596
+ # convert self to LongDecimal
1813
1597
  #
1814
- def initialize(first, second)
1815
- if ((first.kind_of? Rational) || (first.kind_of? Integer)) && (second.kind_of? Integer) then
1816
- @rat = Rational(first.numerator, first.denominator)
1817
- @scale = second
1818
- elsif (first.kind_of? LongDecimal) && (second.kind_of? LongDecimal) then
1819
- orig_scale = first.scale
1820
- first, second = first.anti_equalize_scale(second)
1821
- @rat = Rational(first.to_i, second.to_i)
1822
- @scale = orig_scale
1823
- else
1824
- raise TypeError, "parameters must be (LongDecimal, LongDecimal) or (Rational, Integer): first=#{first.inspect} second=#{second.inspect}";
1825
- end
1598
+ def to_ld
1599
+ LongDecimal(self)
1826
1600
  end
1827
1601
 
1828
- attr_reader :scale, :rat
1602
+ end # Numeric
1603
+
1604
+ #
1605
+ # LongMath provides some helper functions to support LongDecimal and
1606
+ # LongDecimalQuot, mostly operating on integers. They are used
1607
+ # internally here, but possibly they can be used elsewhere as well.
1608
+ # In addition LongMath provides methods like those in Math, but for
1609
+ # LongDecimal instead of Float.
1610
+ #
1611
+ module LongMath
1829
1612
 
1830
- # def scale
1831
- # @scale
1832
- # end
1613
+ include LongDecimalRoundingMode
1833
1614
 
1834
- # def rat
1835
- # @rat
1836
- # end
1615
+ MAX_FLOATABLE = Float::MAX.to_i
1616
+ MAX_EXP_ABLE = Math.log(MAX_FLOATABLE).to_i
1617
+ LOG2 = Math.log(2.0)
1618
+ LOG10 = Math.log(10.0)
1837
1619
 
1838
1620
  #
1839
- # numerator of the included rational number.
1840
- # LongDecimals should duck type like Rationals
1621
+ # helper method for internal use: checks if word_len is a reasonable
1622
+ # size for splitting a number into parts
1841
1623
  #
1842
- def numerator
1843
- rat.numerator
1624
+ def LongMath.check_word_len(word_len, name="word_len")
1625
+ raise TypeError, "#{name} must be a positive number <= 1024" unless (word_len.kind_of? Fixnum) && word_len > 0 && word_len <= 1024
1626
+ word_len
1844
1627
  end
1845
1628
 
1846
1629
  #
1847
- # denominator of the included rational number.
1848
- # LongDecimals should duck type like Rationals
1630
+ # helper method for internal use: checks if parameter x is an Integer
1849
1631
  #
1850
- def denominator
1851
- rat.denominator
1632
+ def LongMath.check_is_int(x, name="x")
1633
+ raise TypeError, "#{name}=#{x.inspect} must be Integer" unless x.kind_of? Integer
1852
1634
  end
1853
1635
 
1854
1636
  #
1855
- # alter scale (only for internal use)
1637
+ # helper method for internal use: checks if parameter x is a LongDecimal
1856
1638
  #
1857
- def scale=(s)
1858
- raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
1859
- raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
1860
- @scale = s
1639
+ def LongMath.check_is_ld(x, name="x")
1640
+ raise TypeError, "x=#{x.inspect} must be LongDecimal" unless x.kind_of? LongDecimal
1861
1641
  end
1862
1642
 
1863
- private :scale=
1864
-
1865
1643
  #
1866
- # conversion to string. Based on the conversion of Rational
1644
+ # helper method for internal use: checks if parameter x is a
1645
+ # reasonable value for the precision (scale) of a LongDecimal
1867
1646
  #
1868
- def to_s
1869
- str = @rat.to_s
1870
- str + "[" + scale.to_s + "]"
1647
+ def LongMath.check_is_prec(prec, name="prec")
1648
+ check_is_int(prec, "prec")
1649
+ raise TypeError, "#{name}=#{prec.inspect} must be >= 0" unless prec >= 0
1871
1650
  end
1872
1651
 
1873
1652
  #
1874
- # conversion to rational
1653
+ # helper method for internal use: checks if parameter x is a
1654
+ # rounding mode (instance of RoundingModeClass)
1875
1655
  #
1876
- def to_r
1877
- Rational(numerator, denominator)
1656
+ def LongMath.check_is_mode(mode, name="mode")
1657
+ raise TypeError, "#{name}=#{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
1878
1658
  end
1879
1659
 
1880
1660
  #
1881
- # convert into Float
1661
+ # split number (Integer) x into parts of word_len bits each such
1662
+ # that the concatenation of these parts as bit patterns is x
1663
+ # (the opposite of merge_from_words)
1882
1664
  #
1883
- def to_f
1884
- to_r.to_f
1665
+ def LongMath.split_to_words(x, word_len = 32)
1666
+ check_word_len(word_len)
1667
+ check_is_int(x, "x")
1668
+ m = x.abs
1669
+ s = (x <=> 0)
1670
+ bit_pattern = (1 << word_len) - 1
1671
+ words = []
1672
+ while (m != 0 || words.length == 0) do
1673
+ w = m & bit_pattern
1674
+ m = m >> word_len
1675
+ words.unshift(w)
1676
+ end
1677
+ if (s < 0) then
1678
+ words[0] = -words[0]
1679
+ end
1680
+ words
1885
1681
  end
1886
1682
 
1887
1683
  #
1888
- # convert into Integer
1684
+ # concatenate numbers given in words as bit patterns
1685
+ # (the opposite of split_to_words)
1889
1686
  #
1890
- def to_i
1891
- to_r.to_i
1687
+ def LongMath.merge_from_words(words, word_len = 32)
1688
+ check_word_len(word_len)
1689
+ raise TypeError, "words must be array of length > 0" unless (words.kind_of? Array) && words.length > 0
1690
+ y = 0
1691
+ s = (words[0] <=> 0)
1692
+ if (s < 0) then
1693
+ words[0] = -words[0]
1694
+ end
1695
+ words.each do |w|
1696
+ y = y << word_len
1697
+ y += w
1698
+ end
1699
+ if (s < 0) then
1700
+ y = -y
1701
+ end
1702
+ y
1892
1703
  end
1893
1704
 
1894
1705
  #
1895
- # conversion to LongDecimal using the internal scale
1706
+
1707
+ # calculate the square root of an integer x using bitwise algorithm
1708
+ # the result is rounded to an integer y such that
1709
+ # y**2�<=�x�<�(y+1)**2
1896
1710
  #
1897
- def to_ld
1898
- round_to_scale(scale, ROUND_HALF_UP)
1711
+ def LongMath.sqrtb(x)
1712
+ a = sqrtb_with_remainder(x)
1713
+ a[0]
1899
1714
  end
1900
1715
 
1901
1716
  #
1902
- # unary plus returns self
1717
+ #�calculate�the�an�integer�s�>=�0�and�a�remainder�r�>=�0�such�that
1718
+ #�x�=�s**2�+�r�and�s**2�<=�x�<�(s+1)**2
1719
+ # the bitwise algorithm is used, which works well for relatively
1720
+ # small values of x.
1903
1721
  #
1904
- def +@
1905
- self
1722
+ def LongMath.sqrtb_with_remainder(x)
1723
+ check_is_int(x, "x")
1724
+
1725
+ s = (x <=> 0)
1726
+ if (s == 0) then
1727
+ return [0, 0]
1728
+ elsif (s < 0)
1729
+ a = sqrtb_with_remainder(-x)
1730
+ return [ Complex(0, a[0]), a[1]]
1731
+ end
1732
+
1733
+ xwords = split_to_words(x, 2)
1734
+ xi = xwords[0] - 1
1735
+ yi = 1
1736
+
1737
+ 1.upto(xwords.length-1) do |i|
1738
+ xi = (xi << 2) + xwords[i]
1739
+ d0 = (yi << 2) + 1
1740
+ r = xi - d0
1741
+ b = 0
1742
+ if (r >= 0) then
1743
+ b = 1
1744
+ xi = r
1745
+ end
1746
+ yi = (yi << 1) + b
1747
+ end
1748
+ return [yi, xi]
1906
1749
  end
1907
1750
 
1908
1751
  #
1909
- # unary minus returns negation of self
1910
- # leaves self unchanged.
1752
+
1753
+ # calculate the square root of an integer using larger chunks of the
1754
+ # number. The optional parameter n provides the size of these
1755
+ # chunks. It is by default chosen to be 16, which is optimized for
1756
+ # 32 bit systems, because internally parts of the double size are
1757
+ # used.
1758
+ # the result is rounded to an integer y such that
1759
+ # y**2�<=�x�<�(y+1)**2
1911
1760
  #
1912
- def -@
1913
- if self.zero? then
1914
- self
1915
- else
1916
- LongDecimalQuot(-rat, scale)
1917
- end
1761
+ def LongMath.sqrtw(x, n = 16)
1762
+ a = sqrtw_with_remainder(x, n)
1763
+ a[0]
1918
1764
  end
1919
1765
 
1920
1766
  #
1921
- # addition
1922
- # if other can be converted into LongDecimalQuot, add as
1923
- # LongDecimalQuot, using the addition of Rationals
1924
- # otherwise use BigDecimal, Complex or Float
1767
+ # calculate the an integer s >= 0 and a remainder r >= 0 such that
1768
+ #�x�=�s**2�+�r�and�s**2�<=�x�<�(s+1)**2
1769
+ # the wordwise algorithm is used, which works well for relatively
1770
+ # large values of x. n defines the word size to be used for the
1771
+ # algorithm. It is good to use half of the machine word, but the
1772
+ # algorithm would also work for other values.
1925
1773
  #
1926
- def +(other)
1927
- o, s = coerce(other)
1928
- if (s.kind_of? LongDecimalQuot) then
1929
- LongDecimalQuot(s.rat + o.rat, [s.scale, o.scale].max)
1930
- else
1931
- s + o
1774
+ def LongMath.sqrtw_with_remainder(x, n = 16)
1775
+ check_is_int(x, "x")
1776
+ check_is_int(n, "n")
1777
+ n2 = n<<1
1778
+ n1 = n+1
1779
+ check_word_len(n2, "2*n")
1780
+
1781
+ s = (x <=> 0)
1782
+ if (s == 0) then
1783
+ return [0, 0]
1784
+ elsif (s < 0)
1785
+ a = sqrtw_with_remainder(-x)
1786
+ return [ Complex(0, a[0]), a[1]]
1932
1787
  end
1933
- end
1934
1788
 
1935
- def -(other)
1936
- o, s = coerce(other)
1937
- if (s.kind_of? LongDecimalQuot) then
1938
- LongDecimalQuot(s.rat - o.rat, [s.scale, o.scale].max)
1939
- else
1940
- s - o
1789
+ xwords = split_to_words(x, n2)
1790
+ if (xwords.length == 1) then
1791
+ return sqrtb_with_remainder(xwords[0])
1941
1792
  end
1942
- end
1943
1793
 
1944
- def *(other)
1945
- o, s = coerce(other)
1946
- if (s.kind_of? LongDecimalQuot) then
1947
- LongDecimalQuot(s.rat * o.rat, s.scale + o.scale)
1948
- else
1949
- s * o
1794
+ # puts(xwords.inspect + "\n")
1795
+ xi = (xwords[0] << n2) + xwords[1]
1796
+ a = sqrtb_with_remainder(xi)
1797
+ yi = a[0]
1798
+ if (xwords.length <= 2) then
1799
+ return a
1800
+ end
1801
+
1802
+ xi -= yi*yi
1803
+ 2.upto(xwords.length-1) do |i|
1804
+ xi = (xi << n2) + xwords[i]
1805
+ d0 = (yi << n1)
1806
+ q = (xi / d0).to_i
1807
+ q0 = q
1808
+ j = 0
1809
+ was_negative = false
1810
+ while (true) do
1811
+ d = d0 + q
1812
+ r = xi - (q * d)
1813
+ break if (0 <= r && (r < d || was_negative))
1814
+ # puts("i=#{i} j=#{j} q=#{q} d0=#{d0} d=#{d} r=#{r} yi=#{yi} xi=#{xi}\n")
1815
+ if (r < 0) then
1816
+ was_negative = true
1817
+ q = q-1
1818
+ else
1819
+ q = q+1
1820
+ end
1821
+ j += 1
1822
+ if (j > 10) then
1823
+ # puts("i=#{i} j=#{j} q=#{q} q0=#{q0} d0=#{d0} d=#{d} r=#{r} yi=#{yi} xi=#{xi}\n")
1824
+ break
1825
+ end
1826
+ end
1827
+ xi = r
1828
+ yi = (yi << n) + q
1950
1829
  end
1830
+ return [ yi, xi ]
1951
1831
  end
1952
1832
 
1953
- def /(other)
1954
- o, s = coerce(other)
1955
- if (s.kind_of? LongDecimalQuot) then
1956
- LongDecimalQuot(s.rat / o.rat, scale)
1957
- else
1958
- s / o
1833
+ #
1834
+
1835
+ # find the gcd of an Integer x with b**n0 where n0 is a sufficiently
1836
+ # high exponent
1837
+ # such that gcd(x, b**m) = gcd(x, b**n) for all m, n >= n0
1838
+ #
1839
+ def LongMath.gcd_with_high_power(x, b)
1840
+ check_is_int(x, "x")
1841
+ raise ZeroDivisionError, "gcd_with_high_power of zero with \"#{b.inspect}\" would be infinity" if x.zero?
1842
+ check_is_int(b, "b")
1843
+ raise ZeroDivisionError, "gcd_with_high_power with b < 2 is not defined. b=\"#{b.inspect}\"" if b < 2
1844
+ s = x.abs
1845
+ exponent = 1
1846
+ b = b.abs
1847
+ if (b < s && s < MAX_FLOATABLE)
1848
+ exponent = (Math.log(s) / Math.log(b)).ceil
1959
1849
  end
1850
+ power = b**exponent
1851
+ result = 1
1852
+ begin
1853
+ f = s.gcd(power)
1854
+ s /= f
1855
+ result *= f
1856
+ end while f > 1
1857
+ result
1960
1858
  end
1961
1859
 
1962
- def **(other)
1963
- if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
1964
- if other.is_int? then
1965
- other = other.to_i
1860
+ #
1861
+ # Find the exponent of the highest power of prime number p that divides
1862
+ # the Integer x. Only works for prime numbers p (parameter prime_number).
1863
+ # The caller has to make sure that p (parameter prime_number) is
1864
+ # actually a prime number, because checks for primality actually cost
1865
+ # something and should not be duplicated more than necessary.
1866
+ # This method works even for numbers x that exceed the range of Float
1867
+ #
1868
+ def LongMath.multiplicity_of_factor(x, prime_number)
1869
+
1870
+ if (x.kind_of? Rational) || (x.kind_of? LongDecimalQuot) then
1871
+ m1 = multiplicity_of_factor(x.numerator, prime_number)
1872
+ m2 = multiplicity_of_factor(x.denominator, prime_number)
1873
+ return m1 - m2
1874
+
1875
+ elsif (x.kind_of? LongDecimal)
1876
+ m1 = multiplicity_of_factor(x.numerator, prime_number)
1877
+ if (prime_number == 2 || prime_number == 5) then
1878
+ return m1 - x.scale
1966
1879
  else
1967
- other = other.to_r
1880
+ return m1
1968
1881
  end
1969
- end
1970
- rat_result = rat ** other
1971
- if (rat_result.kind_of? Rational) then
1972
- if (other.kind_of? Integer) && other >= 0 then
1973
- new_scale = scale * other
1882
+
1883
+ elsif (x.kind_of? Integer)
1884
+
1885
+ power = gcd_with_high_power(x, prime_number)
1886
+ if (power.abs < MAX_FLOATABLE) then
1887
+ result = (Math.log(power) / Math.log(prime_number)).round
1974
1888
  else
1975
- new_scale = scale
1889
+ e = (Math.log(MAX_FLOATABLE) / Math.log(prime_number)).floor
1890
+ result = 0
1891
+ partial = prime_number ** e
1892
+ while (power > partial) do
1893
+ power /= partial
1894
+ result += e
1895
+ end
1896
+ result += (Math.log(power) / Math.log(prime_number)).round
1976
1897
  end
1977
- LongDecimalQuot(rat_result, new_scale)
1898
+ return result
1978
1899
  else
1979
- rat_result
1900
+ raise TypeError, "type of x is not supported #{x.class} #{x.inpect}"
1980
1901
  end
1981
1902
  end
1982
1903
 
1983
- def divmod(other)
1984
- if (other.kind_of? Complex) then
1985
- raise TypeError, "divmod not supported for Complex"
1986
- end
1987
- q = (self / other).to_i
1988
- return q, self - other * q
1989
- end
1904
+ #
1905
+ # method for calculating pi to the given number of digits after the
1906
+ # decimal point.
1907
+ # It works fine for 1000 or 2000 digits or so.
1908
+ # This method could be optimized more, but if you really want to go
1909
+ # for more digits, you will find a specialized and optimized program
1910
+ # for this specific purpose, probably written in C or C++.
1911
+ # Since calculation of pi is not what should typically be done with
1912
+ # LongDecimal, you may consider this method to be the easter egg of
1913
+ # LongDecimal. ;-)
1914
+ #
1915
+ def LongMath.calc_pi(prec, final_mode = LongDecimal::ROUND_HALF_DOWN)
1916
+ mode = LongDecimal::ROUND_HALF_DOWN
1917
+ iprec = 5*(prec+1)
1918
+ sprec = (iprec >> 1) + 1
1919
+ dprec = (prec+1) << 1
1990
1920
 
1991
- def %(other)
1992
- q, r = divmod other
1993
- r
1994
- end
1921
+ a = LongDecimal(1)
1922
+ b = (1 / LongDecimal(2).sqrt(iprec,mode)).round_to_scale(iprec, mode)
1923
+ c = LongDecimal(5,1)
1924
+ k = 1
1925
+ pow_k = 2
1995
1926
 
1996
- # def %(other)
1997
- # o, s = coerce(other)
1998
- # if (s.kind_of? LongDecimalQuot) then
1999
- # LongDecimalQuot(s.rat % o.rat, scale)
2000
- # else
2001
- # s % o
2002
- # end
2003
- # end
1927
+ pi = 0
1928
+ last_pi = 0
1929
+ last_diff = 1
2004
1930
 
2005
- def square
2006
- self * self
1931
+ loop do
1932
+ a, b = ((a + b) / 2).round_to_scale(sprec, mode), (a * b).round_to_scale(iprec, mode).sqrt(sprec, mode)
1933
+ c = (c - pow_k * (a * a - b * b)).round_to_scale(iprec, mode)
1934
+ pi = (2 * a * a / c).round_to_scale(sprec, mode)
1935
+ diff = (pi - last_pi).round_to_scale(dprec, mode).abs
1936
+ if (diff.zero? && last_diff.zero?) then
1937
+ break
1938
+ end
1939
+ last_pi = pi
1940
+ last_diff = diff
1941
+ k += 1
1942
+ pow_k = pow_k << 1
1943
+ # puts("k=#{k} pi=#{pi.to_s}\nd=#{diff}\n\n")
1944
+ end
1945
+ pi.round_to_scale(prec, final_mode)
2007
1946
  end
2008
1947
 
2009
1948
  #
2010
- # calculate the multiplicative inverse
1949
+ # calc the exponential function of x to the given precision as
1950
+ # LongDecimal. Only supports values of x such that the result still
1951
+ # fits into a float (x <= 709). This limitation is somewhat
1952
+ # arbitrary, but it is enforced in order to avoid producing numbers
1953
+ # with the exponential function that exceed the memory. It may be
1954
+ # removed in future versions.
2011
1955
  #
2012
- def reciprocal
2013
- 1 / self
1956
+ def LongMath.exp(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
1957
+ check_is_ld(x, "x")
1958
+ raise TypeError, "x=#{x.inspect} must not be greater #{MAX_EXP_ABLE}" unless x <= MAX_EXP_ABLE
1959
+ check_is_prec(prec, "prec")
1960
+ check_is_mode(mode, "mode")
1961
+ exp_internal(x, prec, mode)
2014
1962
  end
2015
1963
 
2016
1964
  #
2017
- # Absolute value
1965
+ # private helper method for exponentiation
1966
+ # calculate internal precision
2018
1967
  #
2019
- def abs
2020
- LongDecimalQuot(rat.abs, scale)
1968
+ def LongMath.calc_iprec_for_exp(x, prec)
1969
+ iprec_extra = 0
1970
+ if (x > 1) then
1971
+ xf = x.to_f
1972
+ iprec_extra = (xf / LOG10).abs
1973
+ end
1974
+ iprec = ((prec+10)*1.20 + iprec_extra).round
1975
+ if (iprec < prec) then
1976
+ iprec = prec
1977
+ end
1978
+ # puts("calc_iprec_for_exp: x=#{x} prec=#{prec} iprec=#{iprec} iprec_extra=#{iprec_extra}\n")
1979
+ iprec
2021
1980
  end
2022
1981
 
2023
- def abs2
2024
- self.abs.square
2025
- end
1982
+ # private :calc_iprec_for_exp
2026
1983
 
2027
1984
  #
2028
- # convert LongDecimalQuot to LongDecimal with the given precision
2029
- # and the given rounding mode
1985
+ # internal functionality of exp. exposes some more parameters, that
1986
+ # should usually be set to defaut values, in order to allow better testing.
1987
+ # do not actually call this method unless you are testing exp.
1988
+ # create a bug report, if the default settings for the parameters do
1989
+ # not work correctly
2030
1990
  #
2031
- def round_to_scale(new_scale = @scale, mode = ROUND_UNNECESSARY)
1991
+ def LongMath.exp_internal(x, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, j = nil, k = nil, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
1992
+ check_is_ld(x, "x")
1993
+ if (prec == nil) then
1994
+ prec = x.scale
1995
+ end
1996
+ check_is_prec(prec, "prec")
2032
1997
 
2033
- raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
2034
- raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
2035
- raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
1998
+ if (final_mode == nil)
1999
+ final_mode = LongDecimal::ROUND_HALF_DOWN
2000
+ end
2001
+ check_is_mode(final_mode, "final_mode")
2002
+ check_is_mode(mode, "mode")
2036
2003
 
2037
- factor = 10**new_scale
2038
- sign_quot = numerator <=> 0
2039
- if sign_quot == 0 then
2040
- return LongDecimal(0, new_scale)
2004
+ # if the result would come out to zero anyway, cut the work
2005
+ xi = x.to_i
2006
+ if (xi < -LongMath::MAX_FLOATABLE) || -((xi.to_f - 1) / LOG10) > prec+1 then
2007
+ return LongDecimal(25, prec+2).round_to_scale(prec, final_mode)
2041
2008
  end
2042
- prod = numerator * factor
2043
- divisor = denominator
2044
- quot, rem = prod.divmod(divisor)
2045
- sign_rem = rem <=> 0
2046
- if (sign_rem == 0)
2047
- return LongDecimal(quot, new_scale)
2009
+
2010
+ if j == nil || k == nil then
2011
+ s1 = (prec * LOG10 / LOG2) ** (1.0/3.0)
2012
+ if (j == nil) then
2013
+ j = s1.round
2014
+ end
2015
+ if (k == nil) then
2016
+ k = (s1 + Math.log([1, prec].max) / LOG2).round
2017
+ end
2018
+ if (x > 1) then
2019
+ k += (Math.log(x.to_f) / LOG2).abs.round
2020
+ end
2048
2021
  end
2049
- raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem <= 0
2050
- if (sign_quot < 0) then
2051
- rem -= divisor
2052
- quot += 1
2053
- sign_rem = rem <=> 0
2054
- raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem >= 0
2022
+ if (j <= 0) then
2023
+ j = 1
2024
+ end
2025
+ if (k < 0) then
2026
+ k = 0
2055
2027
  end
2028
+ check_is_int(j, "j")
2029
+ check_is_int(k, "k")
2056
2030
 
2057
- if mode == ROUND_UNNECESSARY then
2058
- raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
2031
+ if (iprec == nil) then
2032
+ iprec = calc_iprec_for_exp(x, prec)
2059
2033
  end
2034
+ check_is_prec(iprec, "iprec")
2035
+ # puts("exp_internal: x=#{x} prec=#{prec} iprec=#{iprec}\n")
2060
2036
 
2061
- if (mode == ROUND_CEILING)
2062
- mode = (sign_quot > 0) ? ROUND_UP : ROUND_DOWN
2063
- elsif (mode == ROUND_FLOOR)
2064
- mode = (sign_quot < 0) ? ROUND_UP : ROUND_DOWN
2065
- else
2066
- abs_rem = rem.abs
2067
- half = (abs_rem << 1) <=> denominator
2068
- if (mode == ROUND_HALF_UP || mode == ROUND_HALF_DOWN || mode == ROUND_HALF_EVEN) then
2069
- if (half < 0) then
2070
- mode = ROUND_DOWN
2071
- elsif half > 0 then
2072
- mode = ROUND_UP
2073
- else
2074
- # half == 0
2075
- if (mode == ROUND_HALF_UP) then
2076
- mode = ROUND_UP
2077
- elsif (mode == ROUND_HALF_DOWN) then
2078
- mode = ROUND_DOWN
2079
- else
2080
- # mode == ROUND_HALF_EVEN
2081
- mode = (quot[0] == 1 ? ROUND_UP : ROUND_DOWN)
2082
- end
2083
- end
2037
+ dprec = [ iprec, (prec + 1) << 1 ].min
2038
+
2039
+ x_k = (x / (1 << k)).round_to_scale(iprec, mode)
2040
+ x_j = (x_k ** j).round_to_scale(iprec, mode)
2041
+ s = [ LongDecimal(0) ] * j
2042
+ t = LongDecimal(1)
2043
+ last_t = 1
2044
+ f = 0
2045
+ loop do
2046
+ j.times do |i|
2047
+ s[i] += t
2048
+ f += 1
2049
+ t = (t / f).round_to_scale(iprec, mode)
2084
2050
  end
2051
+ t = (t * x_j).round_to_scale(iprec, mode)
2052
+ break if (t.zero?)
2053
+ tr = t.round_to_scale(dprec, LongDecimal::ROUND_DOWN).abs
2054
+ break if (t.zero?)
2055
+ tu = t.unit
2056
+ break if (tr <= tu && last_t <= tu)
2057
+ last_t = tr
2085
2058
  end
2086
-
2087
- if mode == ROUND_UP
2088
- quot += sign_quot
2059
+ x_i = 1
2060
+ y_k = LongDecimal(0)
2061
+ j.times do |i|
2062
+ if (i > 0) then
2063
+ x_i = (x_i * x_k).round_to_scale(iprec, mode)
2064
+ end
2065
+ # puts("y_k=#{y_k}\ni=#{i} j=#{j} k=#{k} x=#{x}\nx_k=#{x_k}\nx_j=#{x_j}\nx_i=#{x_i}\ns[i]=#{s[i]}\n\n")
2066
+ y_k += (s[i] * x_i).round_to_scale(iprec, mode)
2089
2067
  end
2090
- new_int_val = quot
2091
- LongDecimal(new_int_val, new_scale)
2092
- end
2093
-
2094
- #
2095
- # prepare binary operation of other with LongDecimalQuot
2096
- # Integer, LongDecimal, Rational and LongDecimalQuot can be
2097
- # expressed as LongDecimalQuot, using the scale of self in case of
2098
- # Integer and Rational. Floats can be approximated by LongDecimals
2099
- # and thus be expressed as LongDecimalQuot
2100
- # In case of BigDecimal, Complex or any unknown type, convert self
2101
- # to BigDecimal or Float.
2102
- #
2103
- def coerce(other)
2104
- if other.kind_of? LongDecimal then
2105
- return LongDecimalQuot(other.to_r, other.scale), self
2106
- elsif other.kind_of? LongDecimalQuot then
2107
- return other, self
2108
- elsif other.kind_of? Rational then
2109
- s = scale
2110
- return LongDecimalQuot(other, s), self
2111
- elsif (other.kind_of? Integer) then
2112
- return LongDecimalQuot(other.to_r, scale), self
2113
- elsif other.kind_of? Float then
2114
- return LongDecimalQuot(other.to_ld.to_r, scale), self
2115
- elsif other.kind_of? BigDecimal then
2116
- s, o = other.coerce(self.to_bd)
2117
- elsif other.kind_of? Numeric then
2118
- s, o = other.coerce(self.to_f)
2119
- return o, s
2120
- else
2121
- raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimalQuot"
2068
+ # puts("y_k = #{y_k}\n")
2069
+ k.times do |i|
2070
+ y_k = y_k.square.round_to_scale(iprec, mode)
2071
+ # puts("i=#{i} y_k = #{y_k}\n")
2122
2072
  end
2073
+ y = y_k.round_to_scale(prec, final_mode)
2074
+ y
2123
2075
  end
2124
2076
 
2125
2077
  #
2126
- # compare two numbers for equality.
2127
- # The LongDecimalQuot self is considered == to other if and only if
2128
- # other is also LongDecimalQuot, expresses the same value and has the
2129
- # same scale.
2130
- # It needs to be observed that scale does not influence the value expressed
2131
- # by the number, but only how rouding is performed by default if no
2132
- # explicit number of digits after the decimal point is given. But
2133
- # scale needs to match for equality.
2078
+ # calculate the natural logarithm function of x to the given precision as
2079
+ # LongDecimal.
2134
2080
  #
2135
- def ==(other)
2136
- (other.kind_of? LongDecimalQuot) && (self <=> other) == 0 && self.scale == other.scale
2081
+ def LongMath.log(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
2082
+ check_is_ld(x, "x")
2083
+ check_is_prec(prec, "prec")
2084
+ check_is_mode(mode, "mode")
2085
+ log_internal(x, prec, mode)
2137
2086
  end
2138
2087
 
2139
2088
  #
2140
- # Compares the two numbers for < and > etc.
2089
+ # calculate the base 10 logarithm of x to the given precision as
2090
+ # LongDecimal.
2141
2091
  #
2142
- def <=> (other)
2143
- diff = (self - other)
2144
- if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
2145
- diff.sgn
2146
- else
2147
- diff <=> 0
2092
+ def LongMath.log10(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
2093
+ check_is_ld(x, "x")
2094
+ check_is_prec(prec, "prec")
2095
+ if (x.one?) then
2096
+ return LongDecimal.zero!(prec)
2148
2097
  end
2098
+ check_is_mode(mode, "mode")
2099
+ iprec = prec + 2
2100
+ id = x.int_digits10
2101
+ xx = x.move_point_left(id)
2102
+ # puts("x=#{x} xx=#{xx} id=#{id} iprec=#{iprec}\n")
2103
+ lnxx = log_internal(xx, iprec, mode)
2104
+ ln10 = log_internal(10.to_ld, iprec, mode)
2105
+ y = id + (lnxx / ln10).round_to_scale(prec, mode)
2106
+ return y
2149
2107
  end
2150
2108
 
2151
2109
  #
2152
- # compare scales with <=>
2110
+ # calculate the base 2 logarithm of x to the given precision as
2111
+ # LongDecimal.
2153
2112
  #
2154
- def scale_ufo(other)
2155
- raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
2156
- self.scale <=> other.scale
2113
+ def LongMath.log2(x, prec, mode = LongDecimal::ROUND_HALF_DOWN)
2114
+ check_is_ld(x, "x")
2115
+ check_is_prec(prec, "prec")
2116
+ if (x.one?) then
2117
+ return LongDecimal.zero!(prec)
2118
+ end
2119
+ check_is_mode(mode, "mode")
2120
+ iprec = prec + 2
2121
+ id = x.int_digits2
2122
+ xx = (x / (1 << id)).round_to_scale(x.scale+id)
2123
+ # puts("x=#{x} xx=#{xx} id=#{id} iprec=#{iprec}\n")
2124
+ lnxx = log_internal(xx, iprec, mode)
2125
+ ln2 = log_internal(2.to_ld, iprec, mode)
2126
+ y = id + (lnxx / ln2).round_to_scale(prec, mode)
2127
+ return y
2157
2128
  end
2158
2129
 
2159
2130
  #
2160
- # check if scales are equal
2131
+ # internal functionality of log. exposes some more parameters, that
2132
+ # should usually be set to defaut values, in order to allow better testing.
2133
+ # do not actually call this method unless you are testing log.
2134
+ # create a bug report, if the default settings for the parameters do
2135
+ # not work correctly
2161
2136
  #
2162
- def scale_equal(other)
2163
- scale_ufo(other).zero?
2164
- end
2137
+ def LongMath.log_internal(x, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
2138
+ check_is_ld(x)
2139
+ raise TypeError, "x=#{x.inspect} must not be positive" unless x > 0
2140
+ if (prec == nil) then
2141
+ prec = x.scale
2142
+ end
2143
+ check_is_prec(prec, "prec")
2144
+ if (x.one?) then
2145
+ return LongDecimal.zero!(prec)
2146
+ end
2165
2147
 
2166
- #
2167
- # is self expressable as an integer without loss of digits?
2168
- #
2169
- def is_int?
2170
- denominator == 1
2171
- end
2148
+ if (final_mode == nil)
2149
+ final_mode = LongDecimal::ROUND_HALF_DOWN
2150
+ end
2151
+ check_is_mode(final_mode, "final_mode")
2152
+ check_is_mode(mode, "mode")
2172
2153
 
2173
- #
2174
- # sign of self
2175
- #
2176
- def sgn
2177
- numerator <=> 0
2178
- end
2179
- alias signum sgn
2180
- alias sign sgn
2154
+ if (iprec == nil) then
2155
+ iprec = ((prec+10)*1.20).round
2156
+ end
2157
+ if (iprec < prec) then
2158
+ iprec = prec
2159
+ end
2160
+ check_is_prec(iprec, "iprec")
2181
2161
 
2182
- #
2183
- # Returns a hash code for the complex number.
2184
- #
2185
- def hash
2186
- rat.hash ^ scale.hash
2187
- end
2162
+ # dprec = [ iprec - 1, (prec + 1) << 1 ].min
2163
+ dprec = iprec - 1
2164
+
2165
+ y = 0
2166
+ s = 1
2167
+ if (x < 1) then
2168
+ # puts("x=#{x} iprec=#{iprec}\n")
2169
+ x = (1 / x).round_to_scale(iprec, mode)
2170
+ s = -1
2171
+ # puts("s=#{s} x=#{x} iprec=#{iprec}\n")
2172
+ end
2173
+ exp_part = 0
2174
+ estimate = 0
2175
+ while (x > MAX_FLOATABLE) do
2176
+ if (exp_part == 0) then
2177
+ estimate = MAX_EXP_ABLE.to_ld
2178
+ exp_part = exp(estimate, iprec)
2179
+ end
2180
+ x = (x / exp_part).round_to_scale(iprec, mode)
2181
+ if (s < 0) then
2182
+ y -= estimate
2183
+ else
2184
+ y += estimate
2185
+ end
2186
+ end
2188
2187
 
2188
+ delta = LongDecimal(1, 3)
2189
+ while (x - 1).abs > delta do
2190
+ # puts("too far from 1: x=#{x}\n")
2191
+ xf = x.to_f
2192
+ # puts("xf=#{xf}\n")
2193
+ mlx = Math.log(xf)
2194
+ # puts("log(xf)=#{mlx}\n")
2195
+ estimate = mlx.to_ld.round_to_scale(20, mode)
2196
+ exp_part = exp(estimate, iprec << 1)
2197
+ # puts("y=#{y} s=#{s} est=#{estimate} part=#{exp_part} x=#{x}\n")
2198
+ x = (x / exp_part).round_to_scale(iprec, mode)
2199
+ # puts("divided by exp_part=#{exp_part}: #{x}\n")
2200
+ if (s < 0) then
2201
+ y -= estimate
2202
+ else
2203
+ y += estimate
2204
+ end
2205
+ # puts("y=#{y} s=#{s} est=#{estimate} part=#{exp_part} x=#{x}\n")
2206
+ end
2189
2207
 
2190
- #
2191
- # Returns "<tt>LongDecimalQuot(<i>int_val</i>, <i>scale</i>, <i>num</i>, <i>denom</i>)</tt>".
2192
- #
2193
- def inspect
2194
- sprintf("LongDecimalQuot(Rational(%s, %s), %s)", numerator.inspect, denominator.inspect, scale.inspect)
2195
- end
2208
+ factor = 1
2209
+ # delta = LongDecimal(1, (iprec.to_f**(1/3)).round)
2210
+ # while (x - 1).abs > delta do
2211
+ # x = sqrt(x)
2212
+ # factor *= 2
2213
+ # end
2196
2214
 
2197
- end # LongDecimalQuot
2215
+ sum = 0
2216
+ z = 1 - x
2217
+ i = 1
2218
+ p = 1.to_ld
2219
+ d = 1.to_ld
2220
+ until p.abs.round_to_scale(dprec, LongDecimal::ROUND_DOWN).zero? do
2221
+ p = (p * z).round_to_scale(iprec, mode)
2222
+ d = (p / i).round_to_scale(iprec, mode)
2223
+ i += 1
2224
+ sum += d
2198
2225
 
2199
- #
2200
- # Creates a LongDecimal number. +a+ and +b+ should be Numeric.
2201
- #
2202
- def LongDecimal(a, b = 0)
2203
- if b == 0 && (a.kind_of? LongDecimal) then
2204
- a
2205
- else
2206
- LongDecimal.new!(a, b)
2207
- end
2208
- end
2226
+ # puts("log_internal: s=#{sum} d=#{d} x=#{x} i=#{i} p=#{p} iprec=#{iprec} dprec=#{dprec}\n") if (i & 0x0f == 0x0f)
2227
+ end
2209
2228
 
2210
- #
2211
- # construct a LongDecimalQuot from the given parameters
2212
- # 1st case: both are LongDecimals
2213
- # 2nd case: first is Rational, second is scale
2214
- #
2215
- def LongDecimalQuot(first, second)
2216
- LongDecimalQuot.new!(first, second)
2217
- end
2229
+ # puts("y=#{y} s=#{s} f=#{factor} sum=#{sum}\n")
2230
+ y -= ((s * factor) * sum).round_to_scale(iprec, mode)
2231
+ # puts("y=#{y} s=#{s} f=#{factor} sum=#{sum}\n")
2232
+ return y.round_to_scale(prec, final_mode)
2218
2233
 
2234
+ end
2219
2235
 
2220
- class Numeric
2236
+ #
2237
+ # calc the power of x with exponent y to the given precision as
2238
+ # LongDecimal. Only supports values of y such that exp(y) still
2239
+ # fits into a float (y <= 709)
2240
+ #
2241
+ def LongMath.power(x, y, prec, mode = LongDecimal::ROUND_HALF_DOWN)
2242
+ check_is_ld(x, "x")
2243
+ check_is_ld(y, "y")
2244
+ raise TypeError, "y=#{y.inspect} must not be greater #{MAX_EXP_ABLE}" unless y <= MAX_EXP_ABLE
2245
+ raise TypeError, "x=#{x.inspect} must not be greater #{MAX_FLOATABLE}" unless x <= MAX_FLOATABLE
2246
+ raise TypeError, "x=#{x.inspect} must not positive" unless x > 0
2247
+ check_is_prec(prec, "prec")
2248
+ check_is_mode(mode, "mode")
2249
+ LongMath.power_internal(x, y, prec, mode)
2250
+ end
2221
2251
 
2222
2252
  #
2223
- # convert self to LongDecimal
2253
+ # internal functionality of exp. exposes some more parameters, that
2254
+ # should usually be set to defaut values, in order to allow better testing.
2255
+ # do not actually call this method unless you are testing exp.
2256
+ # create a bug report, if the default settings for the parameters do
2257
+ # not work correctly
2224
2258
  #
2225
- def to_ld
2226
- LongDecimal(self)
2259
+ def LongMath.power_internal(x, y, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
2260
+ check_is_ld(x, "x")
2261
+ if (prec == nil) then
2262
+ prec = x.scale
2263
+ end
2264
+ check_is_prec(prec, "prec")
2265
+
2266
+ if (final_mode == nil)
2267
+ final_mode = LongDecimal::ROUND_HALF_DOWN
2268
+ end
2269
+ check_is_mode(final_mode, "final_mode")
2270
+ check_is_mode(mode, "mode")
2271
+
2272
+ logx_y_f = Math.log(x.to_f) * (y.to_f)
2273
+
2274
+ # iprec = (prec * 1.2 + 20 + (y.abs.to_f) * 1.5 * x.int_digits2).round
2275
+ if (iprec == nil) then
2276
+ iprec = calc_iprec_for_exp(logx_y_f, prec) + 2
2277
+ end
2278
+ # puts("power_internal: x=#{x} y=#{y} logx_y=#{logx_y_f} iprec=#{iprec} prec=#{prec}\n")
2279
+ logx = log(x, iprec, mode)
2280
+ logx_y = logx*y
2281
+ xy = exp_internal(logx_y, prec + 1, mode)
2282
+ # puts("power_internal: x=#{x} logx=#{logx} y=#{y} logx_y=#{logx_y} xy=#{xy} iprec=#{iprec} prec=#{prec}\n")
2283
+ xy.round_to_scale(prec, final_mode)
2227
2284
  end
2228
2285
 
2229
- end # Numeric
2286
+ end # LongMath
2230
2287
 
2231
2288
  # end of file long-decimal.rb