long-decimal 0.00.14 → 0.00.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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