long-decimal 0.00.06
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +33 -0
- data/Rakefile +88 -0
- data/VERSION +1 -0
- data/install.rb +12 -0
- data/lib/longdecimal.rb +1319 -0
- data/make_doc.rb +6 -0
- data/test/testlongdecimal.rb +1731 -0
- data/version.rb +36 -0
- metadata +54 -0
data/lib/longdecimal.rb
ADDED
|
@@ -0,0 +1,1319 @@
|
|
|
1
|
+
#
|
|
2
|
+
# longdecimal.rb -- Arbitrary precision decimals with fixed decimal point
|
|
3
|
+
#
|
|
4
|
+
# CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/longdecimal.rb,v 1.2 2006/02/25 20:05:53 bk1 Exp $
|
|
5
|
+
# CVS-Label: $Name: PRE_ALPHA_0_06 $
|
|
6
|
+
# Author: $Author: bk1 $ (Karl Brodowsky)
|
|
7
|
+
#
|
|
8
|
+
require "complex"
|
|
9
|
+
require "rational"
|
|
10
|
+
# require "bigdecimal"
|
|
11
|
+
# require "bigdecimal/math"
|
|
12
|
+
|
|
13
|
+
#
|
|
14
|
+
# add a functionality to find gcd with a high power of some number
|
|
15
|
+
# probably Integer is not the right place for this stuff, because it
|
|
16
|
+
# is quite special and should go to some kind of Math-like class in the
|
|
17
|
+
# future.
|
|
18
|
+
#
|
|
19
|
+
class Integer
|
|
20
|
+
|
|
21
|
+
MAX_FLOATABLE = Float::MAX.to_i
|
|
22
|
+
MIN_FLOATABLE = Float::MIN.to_i
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# find the gcd of self with b^n0 where n0 is a sufficiently high
|
|
26
|
+
# exponent such that gcd(self, b^m) = gcd(self, b^n)
|
|
27
|
+
# for all m, n > n0
|
|
28
|
+
#
|
|
29
|
+
def gcd_with_high_power(b)
|
|
30
|
+
raise ZeroDivisionError, "gcd_with_high_power of zero with \"#{b.inspect}\" would be infinity" if self.zero?
|
|
31
|
+
raise TypeError, "gcd_with_high_power can only be calculated for integers \"#{b.inspect}\" is no integer" unless b.kind_of? Integer
|
|
32
|
+
raise ZeroDivisionError, "gcd_with_high_power with b < 2 is not defined. b=\"#{b.inspect}\"" if b < 2
|
|
33
|
+
s = self.abs
|
|
34
|
+
exponent = 1
|
|
35
|
+
b = b.abs
|
|
36
|
+
if (b < s && s < MAX_FLOATABLE)
|
|
37
|
+
exponent = (Math.log(s) / Math.log(b)).ceil
|
|
38
|
+
end
|
|
39
|
+
power = b**exponent
|
|
40
|
+
result = 1
|
|
41
|
+
begin
|
|
42
|
+
f = s.gcd(power)
|
|
43
|
+
s /= f
|
|
44
|
+
result *= f
|
|
45
|
+
end while f > 1
|
|
46
|
+
result
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
# find the exponent of the highest power of prime number p that divides
|
|
51
|
+
# self. Only works for prime numbers
|
|
52
|
+
# works even for numbers that exceed the range of Float
|
|
53
|
+
#
|
|
54
|
+
def multiplicity_of_factor(prime_number)
|
|
55
|
+
power = gcd_with_high_power(prime_number)
|
|
56
|
+
if (power.abs < MAX_FLOATABLE) then
|
|
57
|
+
result = (Math.log(power) / Math.log(prime_number)).round
|
|
58
|
+
else
|
|
59
|
+
e = (Math.log(MAX_FLOATABLE) / Math.log(prime_number)).floor
|
|
60
|
+
result = 0
|
|
61
|
+
partial = prime_number ** e
|
|
62
|
+
while (power > partial) do
|
|
63
|
+
power /= partial
|
|
64
|
+
result += e
|
|
65
|
+
end
|
|
66
|
+
result += (Math.log(power) / Math.log(prime_number)).round
|
|
67
|
+
# result = (BigMath.log(BigDecimal(power.to_s + ".0", power.size)) / BigMath.log(BigDecimal(prime_number.to_s + ".0", prime_number.size))).round
|
|
68
|
+
# raise TypeError, "numbers are too big p=#{prime_number} power=#{power}"
|
|
69
|
+
end
|
|
70
|
+
result
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
#
|
|
75
|
+
# add some functionality to Rational.
|
|
76
|
+
# probably Rational is not the right place for this stuff, because it
|
|
77
|
+
# is quite special and should go to some kind of Math-like class in the
|
|
78
|
+
# future.
|
|
79
|
+
#
|
|
80
|
+
class Rational
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
# find the exponent of the highest power of b that divides
|
|
84
|
+
# self. Count negative, if it divides the denominator
|
|
85
|
+
# Only works for prime numbers
|
|
86
|
+
# @todo: needs some improvements, in order to work well for numbers
|
|
87
|
+
# that exceed the range of Float
|
|
88
|
+
#
|
|
89
|
+
def multiplicity_of_factor(prime_number)
|
|
90
|
+
m1 = numerator.multiplicity_of_factor(prime_number)
|
|
91
|
+
m2 = denominator.multiplicity_of_factor(prime_number)
|
|
92
|
+
m1 - m2
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
#
|
|
98
|
+
# define rounding modes to be used for LongDecimal
|
|
99
|
+
# this serves the purpose of an "enum" in C/C++
|
|
100
|
+
#
|
|
101
|
+
module LongDecimalRoundingMode
|
|
102
|
+
RoundingModeClass = Struct.new(:name, :num)
|
|
103
|
+
class RoundingModeClass
|
|
104
|
+
include Comparable
|
|
105
|
+
|
|
106
|
+
#
|
|
107
|
+
# introduce some ordering for rounding modes
|
|
108
|
+
#
|
|
109
|
+
def <=>(o)
|
|
110
|
+
if o.respond_to?:num
|
|
111
|
+
self.num <=> o.num
|
|
112
|
+
else
|
|
113
|
+
self.num <=> o
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# rounding modes as constants
|
|
119
|
+
#
|
|
120
|
+
ROUND_UP = RoundingModeClass.new(:ROUND_UP, 0)
|
|
121
|
+
ROUND_DOWN = RoundingModeClass.new(:ROUND_DOWN, 1)
|
|
122
|
+
ROUND_CEILING = RoundingModeClass.new(:ROUND_CEILING, 2)
|
|
123
|
+
ROUND_FLOOR = RoundingModeClass.new(:ROUND_FLOOR, 3)
|
|
124
|
+
ROUND_HALF_UP = RoundingModeClass.new(:ROUND_HALF_UP, 4)
|
|
125
|
+
ROUND_HALF_DOWN = RoundingModeClass.new(:ROUND_HALF_DOWN, 5)
|
|
126
|
+
ROUND_HALF_EVEN = RoundingModeClass.new(:ROUND_HALF_EVEN, 6)
|
|
127
|
+
ROUND_UNNECESSARY = RoundingModeClass.new(:ROUND_UNNECESSARY, 7)
|
|
128
|
+
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
#
|
|
132
|
+
# class for holding fixed point long decimal numbers
|
|
133
|
+
# these can be considered as a pair of two integer. One contains the
|
|
134
|
+
# digits and the other one the position of the decimal point.
|
|
135
|
+
#
|
|
136
|
+
class LongDecimal < Numeric
|
|
137
|
+
@RCS_ID='-$Id: longdecimal.rb,v 1.2 2006/02/25 20:05:53 bk1 Exp $-'
|
|
138
|
+
|
|
139
|
+
include LongDecimalRoundingMode
|
|
140
|
+
|
|
141
|
+
# MINUS_ONE = LongDecimal(-1)
|
|
142
|
+
# ZERO = LongDecimal(0)
|
|
143
|
+
# ONE = LongDecimal(1)
|
|
144
|
+
# TWO = LongDecimal(2)
|
|
145
|
+
# TEN = LongDecimal(10)
|
|
146
|
+
|
|
147
|
+
#
|
|
148
|
+
# initialization
|
|
149
|
+
# parameters:
|
|
150
|
+
# 1. LongDecimal.new!(x) where x is a string or a number
|
|
151
|
+
# the resulting LongDecimal holds the number x, possibly rounded
|
|
152
|
+
# 2. LongDecimal.new!(x, s) where x is a string or a number and s is the scale
|
|
153
|
+
# the resulting LongDecimal holds the number x / 10**s
|
|
154
|
+
def LongDecimal.new!(x, s = 0)
|
|
155
|
+
new(x, s)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
#
|
|
159
|
+
# creates a LongDecimal representing zero with the given number of
|
|
160
|
+
# digits after the decimal point (scale=s)
|
|
161
|
+
#
|
|
162
|
+
def LongDecimal.zero!(s = 0)
|
|
163
|
+
new(0, s)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
#
|
|
168
|
+
# creates a LongDecimal representing one with the given number of
|
|
169
|
+
# digits after the decimal point (scale=s)
|
|
170
|
+
#
|
|
171
|
+
def LongDecimal.one!(s = 0)
|
|
172
|
+
new(1, s)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
#
|
|
177
|
+
# creates a LongDecimal representing two with the given number of
|
|
178
|
+
# digits after the decimal point (scale=s)
|
|
179
|
+
#
|
|
180
|
+
def LongDecimal.two!(s = 0)
|
|
181
|
+
new(2, s)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
#
|
|
186
|
+
# creates a LongDecimal representing ten with the given number of
|
|
187
|
+
# digits after the decimal point (scale=s)
|
|
188
|
+
#
|
|
189
|
+
def LongDecimal.ten!(s = 0)
|
|
190
|
+
new(10, s)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
#
|
|
195
|
+
# creates a LongDecimal representing minus one with the given number of
|
|
196
|
+
# digits after the decimal point (scale=s)
|
|
197
|
+
#
|
|
198
|
+
def LongDecimal.minus_one!(s = 0)
|
|
199
|
+
new(-1, s)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
#
|
|
204
|
+
# creates a LongDecimal representing a power of ten with the given
|
|
205
|
+
# exponent e and with the given number of digits after the decimal
|
|
206
|
+
# point (scale=s)
|
|
207
|
+
#
|
|
208
|
+
def LongDecimal.power_of_ten!(e, s = 0)
|
|
209
|
+
new(10**e, s)
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
#
|
|
214
|
+
# initialization
|
|
215
|
+
# parameters:
|
|
216
|
+
# LongDecimal.new(x, s) where x is a string or a number and s is the scale
|
|
217
|
+
# the resulting LongDecimal holds the number x / 10**s
|
|
218
|
+
#
|
|
219
|
+
def initialize(x, s)
|
|
220
|
+
|
|
221
|
+
# handle some obvious errors with x first
|
|
222
|
+
raise TypeError, "non numeric 1st arg \"#{x.inspect}\"" if ! (x.kind_of? Numeric) && ! (x.kind_of? String)
|
|
223
|
+
# we could maybe even work with complex number, if their imaginary part is zero.
|
|
224
|
+
# but this is not so important to deal with, so we raise an error anyway.
|
|
225
|
+
raise TypeError, "complex numbers not supported \"#{x.inspect}\"" if x.kind_of? Complex
|
|
226
|
+
|
|
227
|
+
# handle some obvious errors with optional second parameter, if present
|
|
228
|
+
raise TypeError, "non integer 2nd arg \"#{s.inspect}\"" if ! s.kind_of? Integer
|
|
229
|
+
raise TypeError, "negative 2nd arg \"#{s.inspect}\"" if s < 0
|
|
230
|
+
|
|
231
|
+
# scale is the second parameter or 0 if it is missing
|
|
232
|
+
scale = s
|
|
233
|
+
# int_val is the integral value that is multiplied by some 10**-n
|
|
234
|
+
int_val = 0
|
|
235
|
+
|
|
236
|
+
if x.kind_of? Integer then
|
|
237
|
+
# integers are trivial to handle
|
|
238
|
+
int_val = x
|
|
239
|
+
|
|
240
|
+
elsif x.kind_of? Rational then
|
|
241
|
+
# rationals are rounded somehow
|
|
242
|
+
# we need to come up with a better rule here.
|
|
243
|
+
# if denominator is any product of powers of 2 and 5, we do not need to round
|
|
244
|
+
denom = x.denominator
|
|
245
|
+
mul_2 = denom.multiplicity_of_factor(2)
|
|
246
|
+
mul_5 = denom.multiplicity_of_factor(5)
|
|
247
|
+
iscale = [mul_2, mul_5].max
|
|
248
|
+
scale += iscale
|
|
249
|
+
denom /= 2 ** mul_2
|
|
250
|
+
denom /= 5 ** mul_5
|
|
251
|
+
iscale2 = Math.log10(denom).ceil
|
|
252
|
+
scale += iscale2
|
|
253
|
+
# int_val = (x * 10 ** scale).to_i
|
|
254
|
+
int_val = (x * 10 ** (iscale2+iscale)).to_i
|
|
255
|
+
|
|
256
|
+
else
|
|
257
|
+
# we assume a string or a floating point number
|
|
258
|
+
# floating point number is converted to string, so we only deal with strings
|
|
259
|
+
num_str = x.to_s
|
|
260
|
+
len = num_str.length
|
|
261
|
+
|
|
262
|
+
# handle the obvious error that string is empty
|
|
263
|
+
raise TypeError, "1st arg must not be empty string. \"#{num_str.inspect}\"" if len == 0
|
|
264
|
+
|
|
265
|
+
# remove spaces and underscores
|
|
266
|
+
num_str.gsub! /\s/, ""
|
|
267
|
+
num_str.gsub! /_/, ""
|
|
268
|
+
|
|
269
|
+
# handle sign
|
|
270
|
+
num_str.gsub! /^\+/, ""
|
|
271
|
+
negative = false
|
|
272
|
+
if num_str.gsub! /^-/, "" then
|
|
273
|
+
negative = true
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# split in parts before and after decimal point
|
|
277
|
+
num_arr = num_str.split /\./
|
|
278
|
+
if num_arr.length > 2 then
|
|
279
|
+
raise TypeError, "1st arg contains more than one . \"#{num_str.inspect}\""
|
|
280
|
+
end
|
|
281
|
+
num_int = num_arr[0]
|
|
282
|
+
num_rem = num_arr[1]
|
|
283
|
+
num_frac = nil
|
|
284
|
+
num_exp = nil
|
|
285
|
+
unless num_rem.nil? then
|
|
286
|
+
num_arr = num_rem.split /[Ee]/
|
|
287
|
+
num_frac = num_arr[0]
|
|
288
|
+
num_exp = num_arr[1]
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
if num_frac.nil? then
|
|
292
|
+
num_frac = ""
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
if num_exp.nil? || num_exp.empty? then
|
|
296
|
+
num_exp = "0"
|
|
297
|
+
end
|
|
298
|
+
num_exp = num_exp.to_i
|
|
299
|
+
iscale = num_frac.length - num_exp
|
|
300
|
+
scale += iscale
|
|
301
|
+
int_val = (num_int + num_frac).to_i
|
|
302
|
+
if negative then
|
|
303
|
+
int_val = -int_val
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
@scale = scale
|
|
307
|
+
@int_val = int_val
|
|
308
|
+
|
|
309
|
+
end # initialize
|
|
310
|
+
|
|
311
|
+
#
|
|
312
|
+
# get the integer value of self, disregarding the decimal point.
|
|
313
|
+
# Mostly for internal use.
|
|
314
|
+
#
|
|
315
|
+
def int_val
|
|
316
|
+
@int_val
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
#
|
|
320
|
+
# get the scale, i.e. the position of the decimal point.
|
|
321
|
+
# Mostly for internal use.
|
|
322
|
+
#
|
|
323
|
+
def scale
|
|
324
|
+
@scale
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
#
|
|
328
|
+
# alter scale
|
|
329
|
+
# only for internal use.
|
|
330
|
+
# changes self
|
|
331
|
+
# use round_to_scale instead
|
|
332
|
+
#
|
|
333
|
+
def scale=(s)
|
|
334
|
+
raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
|
|
335
|
+
raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
|
|
336
|
+
|
|
337
|
+
# do not work too hard, if scale does not really change.
|
|
338
|
+
unless @scale == s then
|
|
339
|
+
# multiply int_val by a power of 10 in order to compensate for
|
|
340
|
+
# the change of scale and to keep number in the same order of magnitude.
|
|
341
|
+
d = s - @scale
|
|
342
|
+
f = 10 ** (d.abs)
|
|
343
|
+
if (d >= 0) then
|
|
344
|
+
@int_val = (@int_val * f).to_i
|
|
345
|
+
else
|
|
346
|
+
# here we actually do rounding
|
|
347
|
+
@int_val = (@int_val / f).to_i
|
|
348
|
+
end
|
|
349
|
+
@scale = s
|
|
350
|
+
end
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
protected :scale=
|
|
354
|
+
|
|
355
|
+
#
|
|
356
|
+
# create copy of self with different scale
|
|
357
|
+
# param1: new_scale new scale for result
|
|
358
|
+
# param2: mode rounding mode to be applied when information is lost
|
|
359
|
+
#
|
|
360
|
+
def round_to_scale(new_scale, mode = ROUND_UNNECESSARY)
|
|
361
|
+
|
|
362
|
+
raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
|
|
363
|
+
raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
|
|
364
|
+
raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
|
|
365
|
+
if @scale == new_scale then
|
|
366
|
+
self
|
|
367
|
+
else
|
|
368
|
+
diff = new_scale - scale
|
|
369
|
+
factor = 10 ** (diff.abs)
|
|
370
|
+
if (diff > 0) then
|
|
371
|
+
# we become more precise, no rounding issues
|
|
372
|
+
new_int_val = int_val * factor
|
|
373
|
+
else
|
|
374
|
+
quot, rem = int_val.divmod(factor)
|
|
375
|
+
if (rem == 0) then
|
|
376
|
+
new_int_val = quot
|
|
377
|
+
elsif (mode == ROUND_UNNECESSARY) then
|
|
378
|
+
raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
|
|
379
|
+
else
|
|
380
|
+
return LongDecimalQuot(self, LongDecimal(1)).round_to_scale(new_scale, mode)
|
|
381
|
+
end
|
|
382
|
+
end
|
|
383
|
+
LongDecimal(new_int_val, new_scale)
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
#
|
|
388
|
+
# convert self into String, which is the decimal representation.
|
|
389
|
+
# Use trailing zeros, if int_val has them.
|
|
390
|
+
#
|
|
391
|
+
def to_s(shown_scale = @scale, mode = ROUND_UNNECESSARY, base = 10)
|
|
392
|
+
if (base == 10) then
|
|
393
|
+
if (shown_scale == @scale)
|
|
394
|
+
to_s_10
|
|
395
|
+
else
|
|
396
|
+
s = self.round_to_scale(shown_scale, mode)
|
|
397
|
+
s.to_s_10
|
|
398
|
+
end
|
|
399
|
+
else
|
|
400
|
+
# base is not 10
|
|
401
|
+
unless (base.kind_of? Integer) && 2 <= base && base <= 36 then
|
|
402
|
+
raise TypeError, "base must be integer between 2 and 36"
|
|
403
|
+
end
|
|
404
|
+
quot = (self.move_point_right(scale) * base ** shown_scale) / 10 ** scale
|
|
405
|
+
p(quot)
|
|
406
|
+
rounded = quot.round_to_scale(0, mode)
|
|
407
|
+
p(rounded)
|
|
408
|
+
rounded.to_s_internal(base, shown_scale)
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
def to_s_10
|
|
413
|
+
to_s_internal(10, scale)
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
def to_s_internal(b, sc)
|
|
417
|
+
sg = sgn
|
|
418
|
+
i = int_val.abs
|
|
419
|
+
str = i.to_s(b)
|
|
420
|
+
if sc > 0 then
|
|
421
|
+
missing = sc - str.length + 1
|
|
422
|
+
if missing > 0 then
|
|
423
|
+
str = ("0" * missing) + str
|
|
424
|
+
end
|
|
425
|
+
str[-sc, 0] = "."
|
|
426
|
+
end
|
|
427
|
+
str = "-" + str if sg < 0
|
|
428
|
+
str
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
protected :to_s_10
|
|
432
|
+
protected :to_s_internal
|
|
433
|
+
|
|
434
|
+
#
|
|
435
|
+
# convert self into Rational
|
|
436
|
+
# this works quite straitforward. use int_val as numerator and a
|
|
437
|
+
# power of 10 as denominator
|
|
438
|
+
#
|
|
439
|
+
def to_r
|
|
440
|
+
Rational(@int_val, 10**@scale)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
#
|
|
444
|
+
# convert self into Float
|
|
445
|
+
# this works straitforward by dividing int_val by power of 10 in
|
|
446
|
+
# float-arithmetic.
|
|
447
|
+
#
|
|
448
|
+
def to_f
|
|
449
|
+
int_val.to_f / 10**scale
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
#
|
|
453
|
+
# convert self into Integer
|
|
454
|
+
# This may loose information. In most cases it is preferred to
|
|
455
|
+
# control this by calling round_to_scale first and then applying
|
|
456
|
+
# to_i when the number represented by self is actually an integer.
|
|
457
|
+
#
|
|
458
|
+
def to_i
|
|
459
|
+
to_r.to_i
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
#
|
|
463
|
+
# convert self into LongDecimal (returns self)
|
|
464
|
+
#
|
|
465
|
+
def to_ld
|
|
466
|
+
self
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
#
|
|
470
|
+
# LongDecimals can be seen as a fraction with a power of 10 as
|
|
471
|
+
# denominator for compatibility with other numeric classes this
|
|
472
|
+
# method is included, returning 10**scale
|
|
473
|
+
#
|
|
474
|
+
def denominator
|
|
475
|
+
10**scale
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
#
|
|
479
|
+
# LongDecimals can be seen as a fraction with its int_val as its
|
|
480
|
+
# numerator
|
|
481
|
+
#
|
|
482
|
+
alias numerator int_val
|
|
483
|
+
|
|
484
|
+
#
|
|
485
|
+
# before adding or subtracting two LongDecimal numbers
|
|
486
|
+
# it is mandatory to set them to the same scale. The maximum of the
|
|
487
|
+
# two summands is used, in order to avoid loosing any information.
|
|
488
|
+
# this method is mostly for internal use
|
|
489
|
+
#
|
|
490
|
+
def equalize_scale(other)
|
|
491
|
+
o, s = coerce(other)
|
|
492
|
+
if (s.kind_of? LongDecimal) then
|
|
493
|
+
# make sure Floats do not mess up our number of significant digits when adding
|
|
494
|
+
if (other.kind_of? Float) then
|
|
495
|
+
o = o.round_to_scale(s.scale, ROUND_HALF_UP)
|
|
496
|
+
else
|
|
497
|
+
new_scale = [s.scale, o.scale].max
|
|
498
|
+
s = s.round_to_scale(new_scale)
|
|
499
|
+
o = o.round_to_scale(new_scale)
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
return s, o
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
#
|
|
506
|
+
# before dividing two LongDecimal numbers, it is mandatory to set
|
|
507
|
+
# make them both to integers, so the result is simply expressable as
|
|
508
|
+
# a rational
|
|
509
|
+
# this method is mostly for internal use
|
|
510
|
+
#
|
|
511
|
+
def anti_equalize_scale(other)
|
|
512
|
+
o, s = coerce(other)
|
|
513
|
+
if (s.kind_of? LongDecimal) then
|
|
514
|
+
exponent = [s.scale, o.scale].max
|
|
515
|
+
factor = 10**exponent
|
|
516
|
+
s *= factor
|
|
517
|
+
o *= factor
|
|
518
|
+
s = s.round_to_scale(0)
|
|
519
|
+
o = o.round_to_scale(0)
|
|
520
|
+
end
|
|
521
|
+
return s, o
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
#
|
|
525
|
+
# successor for ranges
|
|
526
|
+
#
|
|
527
|
+
def succ
|
|
528
|
+
LongDecimal(int_val + 1, scale)
|
|
529
|
+
end
|
|
530
|
+
|
|
531
|
+
alias next succ
|
|
532
|
+
|
|
533
|
+
#
|
|
534
|
+
# predecessor
|
|
535
|
+
#
|
|
536
|
+
def pred
|
|
537
|
+
LongDecimal(int_val - 1, scale)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
#
|
|
541
|
+
# self + 1
|
|
542
|
+
#
|
|
543
|
+
def inc
|
|
544
|
+
self + 1
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
#
|
|
548
|
+
# self - 1
|
|
549
|
+
#
|
|
550
|
+
def dec
|
|
551
|
+
self - 1
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
#
|
|
555
|
+
# return the unit by which self is incremented by succ
|
|
556
|
+
#
|
|
557
|
+
def unit
|
|
558
|
+
LongDecimal(1, scale)
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
#
|
|
562
|
+
# apply unary +
|
|
563
|
+
# (returns self)
|
|
564
|
+
#
|
|
565
|
+
def +@
|
|
566
|
+
self
|
|
567
|
+
end
|
|
568
|
+
|
|
569
|
+
#
|
|
570
|
+
# apply unary -
|
|
571
|
+
# (returns negated self)
|
|
572
|
+
#
|
|
573
|
+
def -@
|
|
574
|
+
if self.zero? then
|
|
575
|
+
self
|
|
576
|
+
else
|
|
577
|
+
LongDecimal(-int_val, scale)
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
#
|
|
582
|
+
# add two numbers
|
|
583
|
+
# if both can immediately be expressed as LongDecimal, the result is
|
|
584
|
+
# a LongDecimal as well. The number of digits after the decimal
|
|
585
|
+
# point is the max of the scales of the summands
|
|
586
|
+
# if LongDecimal does not cover the two summands, call addition of
|
|
587
|
+
# Complex, Float or LongRationalQuot
|
|
588
|
+
#
|
|
589
|
+
def +(other)
|
|
590
|
+
s, o = equalize_scale(other)
|
|
591
|
+
if s.kind_of? LongDecimal then
|
|
592
|
+
LongDecimal(s.int_val + o.int_val, s.scale)
|
|
593
|
+
else
|
|
594
|
+
s + o
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
#
|
|
599
|
+
# subtract two numbers
|
|
600
|
+
# if both can immediately be expressed as LongDecimal, the result is
|
|
601
|
+
# a LongDecimal as well. The number of digits after the decimal
|
|
602
|
+
# point is the max of the scales of self and other.
|
|
603
|
+
# if LongDecimal does not cover self and other, the subtraction of
|
|
604
|
+
# Complex, Float or LongRationalQuot is used
|
|
605
|
+
#
|
|
606
|
+
def -(other)
|
|
607
|
+
s, o = equalize_scale(other)
|
|
608
|
+
if s.kind_of? LongDecimal then
|
|
609
|
+
LongDecimal(s.int_val - o.int_val, s.scale)
|
|
610
|
+
else
|
|
611
|
+
s - o
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
|
|
615
|
+
#
|
|
616
|
+
# multiply two numbers
|
|
617
|
+
# if both can immediately be expressed as LongDecimal, the result is
|
|
618
|
+
# a LongDecimal as well. The number of digits after the decimal
|
|
619
|
+
# point is the sum of the scales of both factors.
|
|
620
|
+
# if LongDecimal does not cover self and other, the multiplication of
|
|
621
|
+
# Complex, Float or LongRationalQuot is used
|
|
622
|
+
#
|
|
623
|
+
def *(other)
|
|
624
|
+
o, s = coerce(other)
|
|
625
|
+
if s.kind_of? LongDecimal then
|
|
626
|
+
LongDecimal(s.int_val * o.int_val, s.scale + o.scale)
|
|
627
|
+
else
|
|
628
|
+
s * o
|
|
629
|
+
end
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def divide(other, rounding_mode)
|
|
633
|
+
divide_s(other, nil, rounding_mode)
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def divide_s(other, new_scale, rounding_mode)
|
|
637
|
+
q = self / other
|
|
638
|
+
if (q.kind_of? Float) then
|
|
639
|
+
q = LongDecimal(q)
|
|
640
|
+
end
|
|
641
|
+
if (q.kind_of? LongDecimal) || (q.kind_of? LongDecimalQuot) then
|
|
642
|
+
if (new_scale.nil?) then
|
|
643
|
+
new_scale = q.scale
|
|
644
|
+
end
|
|
645
|
+
q.round_to_scale(new_scale, rounding_mode)
|
|
646
|
+
else
|
|
647
|
+
q
|
|
648
|
+
end
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
def rdiv(other)
|
|
652
|
+
q = self / other
|
|
653
|
+
if (q.kind_of? LongDecimalQuot) || (q.kind_of? LongDecimalQuot) then
|
|
654
|
+
r.to_r
|
|
655
|
+
else
|
|
656
|
+
q
|
|
657
|
+
end
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
def /(other)
|
|
661
|
+
o, s = coerce(other)
|
|
662
|
+
if (s.kind_of? LongDecimal) then
|
|
663
|
+
LongDecimalQuot(s, o)
|
|
664
|
+
else
|
|
665
|
+
s / o
|
|
666
|
+
end
|
|
667
|
+
end
|
|
668
|
+
|
|
669
|
+
def **(other)
|
|
670
|
+
if ((other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)) && other.is_int? then
|
|
671
|
+
other = other.to_i
|
|
672
|
+
end
|
|
673
|
+
if other.kind_of? Integer then
|
|
674
|
+
if other >= 0 then
|
|
675
|
+
LongDecimal(int_val ** other, scale * other)
|
|
676
|
+
else
|
|
677
|
+
abs_other = -other
|
|
678
|
+
new_scale = abs_other * scale
|
|
679
|
+
LongDecimalQuot(Rational(10 ** new_scale, int_val ** abs_other), new_scale)
|
|
680
|
+
end
|
|
681
|
+
else
|
|
682
|
+
if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
|
|
683
|
+
other = other.to_f
|
|
684
|
+
end
|
|
685
|
+
self.to_f ** other
|
|
686
|
+
end
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
def divmod(other)
|
|
690
|
+
if (other.kind_of? Complex) then
|
|
691
|
+
raise TypeError, "divmod not supported for Complex"
|
|
692
|
+
end
|
|
693
|
+
q = (self / other).to_i
|
|
694
|
+
return q, self - other * q
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
#
|
|
698
|
+
# find the exponent of the highest power of prime number p that divides
|
|
699
|
+
# self. Only works for prime numbers
|
|
700
|
+
# works even for numbers that exceed the range of Float
|
|
701
|
+
#
|
|
702
|
+
def multiplicity_of_factor(prime_number)
|
|
703
|
+
m1 = numerator.multiplicity_of_factor(prime_number)
|
|
704
|
+
if (prime_number == 2 || prime_number == 5) then
|
|
705
|
+
m1 - scale
|
|
706
|
+
else
|
|
707
|
+
m1
|
|
708
|
+
end
|
|
709
|
+
end
|
|
710
|
+
|
|
711
|
+
def %(other)
|
|
712
|
+
q, r = divmod other
|
|
713
|
+
r
|
|
714
|
+
end
|
|
715
|
+
|
|
716
|
+
#
|
|
717
|
+
# performs bitwise AND between self and Numeric
|
|
718
|
+
#
|
|
719
|
+
def &(other)
|
|
720
|
+
s, o = equalize_scale(other)
|
|
721
|
+
if s.kind_of? LongDecimal then
|
|
722
|
+
LongDecimal(s.int_val & o.int_val, s.scale)
|
|
723
|
+
else
|
|
724
|
+
s & o
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
|
|
728
|
+
#
|
|
729
|
+
# performs bitwise OR between self and Numeric
|
|
730
|
+
#
|
|
731
|
+
def |(other)
|
|
732
|
+
s, o = equalize_scale(other)
|
|
733
|
+
if s.kind_of? LongDecimal then
|
|
734
|
+
LongDecimal(s.int_val | o.int_val, s.scale)
|
|
735
|
+
else
|
|
736
|
+
s | o
|
|
737
|
+
end
|
|
738
|
+
end
|
|
739
|
+
|
|
740
|
+
#
|
|
741
|
+
# performs bitwise XOR between self and Numeric
|
|
742
|
+
#
|
|
743
|
+
def ^(other)
|
|
744
|
+
s, o = equalize_scale(other)
|
|
745
|
+
if s.kind_of? LongDecimal then
|
|
746
|
+
LongDecimal(s.int_val ^ o.int_val, s.scale)
|
|
747
|
+
else
|
|
748
|
+
s ^ o
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
#
|
|
753
|
+
# bitwise inversion
|
|
754
|
+
#
|
|
755
|
+
def ~
|
|
756
|
+
LongDecimal(~int_val, scale)
|
|
757
|
+
end
|
|
758
|
+
|
|
759
|
+
#
|
|
760
|
+
# performs bitwise left shift of self by Numeric
|
|
761
|
+
#
|
|
762
|
+
def <<(other)
|
|
763
|
+
unless (other.kind_of? Integer) && other >= 0 then
|
|
764
|
+
raise TypeError, "cannot shift by something other than integer >= 0"
|
|
765
|
+
end
|
|
766
|
+
LongDecimal(s.int_val << other, s.scale)
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
#
|
|
770
|
+
# performs bitwise right shift of self by Numeric
|
|
771
|
+
#
|
|
772
|
+
def >>(other)
|
|
773
|
+
unless (other.kind_of? Integer) && other >= 0 then
|
|
774
|
+
raise TypeError, "cannot shift by something other than integer >= 0"
|
|
775
|
+
end
|
|
776
|
+
LongDecimal(s.int_val >> other, s.scale)
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
#
|
|
780
|
+
# gets binary digit
|
|
781
|
+
#
|
|
782
|
+
def [](other)
|
|
783
|
+
int_val[other]
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
#
|
|
787
|
+
# gets size of int_val
|
|
788
|
+
#
|
|
789
|
+
def size
|
|
790
|
+
int_val.size
|
|
791
|
+
end
|
|
792
|
+
|
|
793
|
+
#
|
|
794
|
+
# divide by 10**n
|
|
795
|
+
#
|
|
796
|
+
def move_point_left(n)
|
|
797
|
+
raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
|
|
798
|
+
if (n >= 0) then
|
|
799
|
+
move_point_left_int(n)
|
|
800
|
+
else
|
|
801
|
+
move_point_right_int(-n)
|
|
802
|
+
end
|
|
803
|
+
end
|
|
804
|
+
|
|
805
|
+
#
|
|
806
|
+
# multiply by 10**n
|
|
807
|
+
#
|
|
808
|
+
def move_point_right(n)
|
|
809
|
+
raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
|
|
810
|
+
if (n < 0) then
|
|
811
|
+
move_point_left_int(-n)
|
|
812
|
+
else
|
|
813
|
+
move_point_right_int(n)
|
|
814
|
+
end
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
#
|
|
818
|
+
# divide by 10**n
|
|
819
|
+
#
|
|
820
|
+
def move_point_left_int(n)
|
|
821
|
+
raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
|
|
822
|
+
LongDecimal(int_val, scale + n)
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
#
|
|
826
|
+
# multiply by 10**n
|
|
827
|
+
#
|
|
828
|
+
def move_point_right_int(n)
|
|
829
|
+
raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
|
|
830
|
+
if (n > scale) then
|
|
831
|
+
LongDecimal(int_val * 10**(n-scale), 0)
|
|
832
|
+
else
|
|
833
|
+
LongDecimal(int_val, scale-n)
|
|
834
|
+
end
|
|
835
|
+
end
|
|
836
|
+
|
|
837
|
+
protected :move_point_left_int, :move_point_right_int
|
|
838
|
+
|
|
839
|
+
#
|
|
840
|
+
# calculate the sqare of self
|
|
841
|
+
#
|
|
842
|
+
def square
|
|
843
|
+
self * self
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
#
|
|
847
|
+
# calculate the multiplicative inverse
|
|
848
|
+
#
|
|
849
|
+
def reciprocal
|
|
850
|
+
1 / self
|
|
851
|
+
end
|
|
852
|
+
|
|
853
|
+
#
|
|
854
|
+
# Absolute value
|
|
855
|
+
#
|
|
856
|
+
def abs
|
|
857
|
+
LongDecimal(int_val.abs, scale)
|
|
858
|
+
end
|
|
859
|
+
|
|
860
|
+
#
|
|
861
|
+
# square of absolute value
|
|
862
|
+
#
|
|
863
|
+
def abs2
|
|
864
|
+
self.abs.square
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
#
|
|
868
|
+
# Compares the absolute values of the two numbers.
|
|
869
|
+
#
|
|
870
|
+
def <=> (other)
|
|
871
|
+
diff = (self - other)
|
|
872
|
+
if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
|
|
873
|
+
diff.sgn
|
|
874
|
+
else
|
|
875
|
+
diff <=> 0
|
|
876
|
+
end
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
def scale_ufo(other)
|
|
880
|
+
raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
|
|
881
|
+
self.scale <=> other.scale
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
def scale_equal(other)
|
|
885
|
+
scale_ufo(other).zero?
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
def coerce(other)
|
|
889
|
+
if other.kind_of? LongDecimal then
|
|
890
|
+
return other, self
|
|
891
|
+
elsif other.kind_of? LongDecimalQuot then
|
|
892
|
+
return other, LongDecimalQuot(self.to_r, scale)
|
|
893
|
+
elsif other.kind_of? Rational then
|
|
894
|
+
s = scale
|
|
895
|
+
return LongDecimalQuot(other, s), LongDecimalQuot(self.to_r, s)
|
|
896
|
+
elsif (other.kind_of? Integer) || (other.kind_of? Float) then
|
|
897
|
+
other = LongDecimal(other)
|
|
898
|
+
if (other.scale > scale) then
|
|
899
|
+
other = other.round_to_scale(scale, ROUND_HALF_UP)
|
|
900
|
+
end
|
|
901
|
+
return other, self
|
|
902
|
+
elsif other.kind_of? Numeric then
|
|
903
|
+
s, o = other.coerce(self.to_f)
|
|
904
|
+
return o, s
|
|
905
|
+
else
|
|
906
|
+
raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimal"
|
|
907
|
+
end
|
|
908
|
+
end
|
|
909
|
+
|
|
910
|
+
# is self expressable as an integer without loss of digits?
|
|
911
|
+
def is_int?
|
|
912
|
+
scale == 0 || int_val % 10**scale == 0
|
|
913
|
+
end
|
|
914
|
+
|
|
915
|
+
def sgn
|
|
916
|
+
int_val <=> 0
|
|
917
|
+
end
|
|
918
|
+
alias signum sgn
|
|
919
|
+
alias sign sgn
|
|
920
|
+
|
|
921
|
+
def ==(other)
|
|
922
|
+
(other.kind_of? LongDecimal) && (self <=> other) == 0 && self.scale == other.scale
|
|
923
|
+
end
|
|
924
|
+
|
|
925
|
+
def zero?
|
|
926
|
+
int_val.zero?
|
|
927
|
+
end
|
|
928
|
+
|
|
929
|
+
#
|
|
930
|
+
# Returns a hash code for the complex number.
|
|
931
|
+
#
|
|
932
|
+
def hash
|
|
933
|
+
int_val.hash ^ scale.hash
|
|
934
|
+
end
|
|
935
|
+
|
|
936
|
+
#
|
|
937
|
+
# Returns "<tt>LongDecimal(<i>int_val</i>, <i>scale</i>)</tt>".
|
|
938
|
+
#
|
|
939
|
+
def inspect
|
|
940
|
+
sprintf("LongDecimal(%s, %s)", int_val.inspect, scale.inspect)
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
#
|
|
947
|
+
# This class is used for storing intermediate results after having
|
|
948
|
+
# performed a division. The division cannot be completed without
|
|
949
|
+
# providing additional information on how to round the result.
|
|
950
|
+
#
|
|
951
|
+
class LongDecimalQuot < Numeric
|
|
952
|
+
|
|
953
|
+
@RCS_ID='-$Id: longdecimal.rb,v 1.2 2006/02/25 20:05:53 bk1 Exp $-'
|
|
954
|
+
|
|
955
|
+
include LongDecimalRoundingMode
|
|
956
|
+
|
|
957
|
+
def LongDecimalQuot.new!(first, second)
|
|
958
|
+
new(first, second)
|
|
959
|
+
end
|
|
960
|
+
|
|
961
|
+
#
|
|
962
|
+
# create a new LongDecimalQuot from a rational and a scale or a
|
|
963
|
+
# pair of LongDecimals
|
|
964
|
+
def initialize(first, second)
|
|
965
|
+
if (first.kind_of? Rational) && (second.kind_of? Integer) then
|
|
966
|
+
@rat = Rational(first.numerator, first.denominator)
|
|
967
|
+
@scale = second
|
|
968
|
+
elsif (first.kind_of? LongDecimal) && (second.kind_of? LongDecimal) then
|
|
969
|
+
orig_scale = first.scale
|
|
970
|
+
first, second = first.anti_equalize_scale(second)
|
|
971
|
+
@rat = Rational(first.to_i, second.to_i)
|
|
972
|
+
@scale = orig_scale
|
|
973
|
+
else
|
|
974
|
+
raise TypeError, "parameters must be (LongDecimal, LongDecimal) or (Rational, Integer): first=#{first.inspect} second=#{second.inspect}";
|
|
975
|
+
end
|
|
976
|
+
end
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
def scale
|
|
980
|
+
@scale
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
def rat
|
|
984
|
+
@rat
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
def numerator
|
|
988
|
+
rat.numerator
|
|
989
|
+
end
|
|
990
|
+
|
|
991
|
+
def denominator
|
|
992
|
+
rat.denominator
|
|
993
|
+
end
|
|
994
|
+
|
|
995
|
+
# alter scale
|
|
996
|
+
def scale=(s)
|
|
997
|
+
raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
|
|
998
|
+
raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
|
|
999
|
+
@scale = s
|
|
1000
|
+
end
|
|
1001
|
+
|
|
1002
|
+
private :scale=
|
|
1003
|
+
|
|
1004
|
+
def to_s
|
|
1005
|
+
str = @rat.to_s
|
|
1006
|
+
str + "[" + scale.to_s + "]"
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
def to_r
|
|
1010
|
+
Rational(numerator, denominator)
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
# convert into Float
|
|
1014
|
+
def to_f
|
|
1015
|
+
to_r.to_f
|
|
1016
|
+
end
|
|
1017
|
+
|
|
1018
|
+
# convert into Integer
|
|
1019
|
+
def to_i
|
|
1020
|
+
to_r.to_i
|
|
1021
|
+
end
|
|
1022
|
+
|
|
1023
|
+
def to_ld
|
|
1024
|
+
LongDecimal(self, scale)
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
def +@
|
|
1028
|
+
self
|
|
1029
|
+
end
|
|
1030
|
+
|
|
1031
|
+
def -@
|
|
1032
|
+
if self.zero? then
|
|
1033
|
+
self
|
|
1034
|
+
else
|
|
1035
|
+
LongDecimalQuot(-rat, scale)
|
|
1036
|
+
end
|
|
1037
|
+
end
|
|
1038
|
+
|
|
1039
|
+
def +(other)
|
|
1040
|
+
o, s = coerce(other)
|
|
1041
|
+
if (s.kind_of? LongDecimalQuot) then
|
|
1042
|
+
LongDecimalQuot(s.rat + o.rat, [s.scale, o.scale].max)
|
|
1043
|
+
else
|
|
1044
|
+
s + o
|
|
1045
|
+
end
|
|
1046
|
+
end
|
|
1047
|
+
|
|
1048
|
+
def -(other)
|
|
1049
|
+
o, s = coerce(other)
|
|
1050
|
+
if (s.kind_of? LongDecimalQuot) then
|
|
1051
|
+
LongDecimalQuot(s.rat - o.rat, [s.scale, o.scale].max)
|
|
1052
|
+
else
|
|
1053
|
+
s - o
|
|
1054
|
+
end
|
|
1055
|
+
end
|
|
1056
|
+
|
|
1057
|
+
def *(other)
|
|
1058
|
+
o, s = coerce(other)
|
|
1059
|
+
if (s.kind_of? LongDecimalQuot) then
|
|
1060
|
+
LongDecimalQuot(s.rat * o.rat, s.scale + o.scale)
|
|
1061
|
+
else
|
|
1062
|
+
s * o
|
|
1063
|
+
end
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
def /(other)
|
|
1067
|
+
o, s = coerce(other)
|
|
1068
|
+
if (s.kind_of? LongDecimalQuot) then
|
|
1069
|
+
LongDecimalQuot(s.rat / o.rat, scale)
|
|
1070
|
+
else
|
|
1071
|
+
s / o
|
|
1072
|
+
end
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
def **(other)
|
|
1076
|
+
if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
|
|
1077
|
+
if other.is_int? then
|
|
1078
|
+
other = other.to_i
|
|
1079
|
+
else
|
|
1080
|
+
other = other.to_r
|
|
1081
|
+
end
|
|
1082
|
+
end
|
|
1083
|
+
rat_result = rat ** other
|
|
1084
|
+
if (rat_result.kind_of? Rational) then
|
|
1085
|
+
if (other.kind_of? Integer) && other >= 0 then
|
|
1086
|
+
new_scale = scale * other
|
|
1087
|
+
else
|
|
1088
|
+
new_scale = scale
|
|
1089
|
+
end
|
|
1090
|
+
LongDecimalQuot(rat_result, new_scale)
|
|
1091
|
+
else
|
|
1092
|
+
rat_result
|
|
1093
|
+
end
|
|
1094
|
+
end
|
|
1095
|
+
|
|
1096
|
+
def divmod(other)
|
|
1097
|
+
if (other.kind_of? Complex) then
|
|
1098
|
+
raise TypeError, "divmod not supported for Complex"
|
|
1099
|
+
end
|
|
1100
|
+
q = (self / other).to_i
|
|
1101
|
+
return q, self - other * q
|
|
1102
|
+
end
|
|
1103
|
+
|
|
1104
|
+
def %(other)
|
|
1105
|
+
q, r = divmod other
|
|
1106
|
+
r
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
# def %(other)
|
|
1110
|
+
# o, s = coerce(other)
|
|
1111
|
+
# if (s.kind_of? LongDecimalQuot) then
|
|
1112
|
+
# LongDecimalQuot(s.rat % o.rat, scale)
|
|
1113
|
+
# else
|
|
1114
|
+
# s % o
|
|
1115
|
+
# end
|
|
1116
|
+
# end
|
|
1117
|
+
|
|
1118
|
+
#
|
|
1119
|
+
# find the exponent of the highest power of prime number p that divides
|
|
1120
|
+
# self. Only works for prime numbers
|
|
1121
|
+
# works even for numbers that exceed the range of Float
|
|
1122
|
+
#
|
|
1123
|
+
def multiplicity_of_factor(prime_number)
|
|
1124
|
+
rat.multiplicity_of_factor(prime_number)
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
def square
|
|
1128
|
+
self * self
|
|
1129
|
+
end
|
|
1130
|
+
|
|
1131
|
+
#
|
|
1132
|
+
# calculate the multiplicative inverse
|
|
1133
|
+
#
|
|
1134
|
+
def reciprocal
|
|
1135
|
+
1 / self
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1138
|
+
#
|
|
1139
|
+
# Absolute value
|
|
1140
|
+
#
|
|
1141
|
+
def abs
|
|
1142
|
+
LongDecimalQuot(rat.abs, scale)
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1145
|
+
def abs2
|
|
1146
|
+
self.abs.square
|
|
1147
|
+
end
|
|
1148
|
+
|
|
1149
|
+
#
|
|
1150
|
+
# convert LongDecimalQuot to LongDecimal with the given precision
|
|
1151
|
+
# and the given rounding mode
|
|
1152
|
+
#
|
|
1153
|
+
def round_to_scale(new_scale = @scale, mode = ROUND_UNNECESSARY)
|
|
1154
|
+
|
|
1155
|
+
raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
|
|
1156
|
+
raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
|
|
1157
|
+
raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
|
|
1158
|
+
|
|
1159
|
+
factor = 10**new_scale
|
|
1160
|
+
sign_quot = numerator <=> 0
|
|
1161
|
+
if sign_quot == 0 then
|
|
1162
|
+
return LongDecimal(0, new_scale)
|
|
1163
|
+
end
|
|
1164
|
+
prod = numerator * factor
|
|
1165
|
+
divisor = denominator
|
|
1166
|
+
quot, rem = prod.divmod(divisor)
|
|
1167
|
+
sign_rem = rem <=> 0
|
|
1168
|
+
# puts("self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem} sign_rem=#{sign_rem.to_s} sign_quot=#{sign_quot.to_s}")
|
|
1169
|
+
if (sign_rem == 0)
|
|
1170
|
+
return LongDecimal(quot, new_scale)
|
|
1171
|
+
end
|
|
1172
|
+
raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem <= 0
|
|
1173
|
+
if (sign_quot < 0) then
|
|
1174
|
+
rem -= divisor
|
|
1175
|
+
quot += 1
|
|
1176
|
+
sign_rem = rem <=> 0
|
|
1177
|
+
raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem >= 0
|
|
1178
|
+
end
|
|
1179
|
+
# puts("self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem} sign_rem=#{sign_rem.to_s} sign_quot=#{sign_quot.to_s}")
|
|
1180
|
+
|
|
1181
|
+
if mode == ROUND_UNNECESSARY then
|
|
1182
|
+
raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
if (mode == ROUND_CEILING)
|
|
1186
|
+
mode = (sign_quot > 0) ? ROUND_UP : ROUND_DOWN
|
|
1187
|
+
elsif (mode == ROUND_FLOOR)
|
|
1188
|
+
mode = (sign_quot < 0) ? ROUND_UP : ROUND_DOWN
|
|
1189
|
+
else
|
|
1190
|
+
abs_rem = rem.abs
|
|
1191
|
+
half = (abs_rem << 1) <=> denominator
|
|
1192
|
+
if (mode == ROUND_HALF_UP || mode == ROUND_HALF_DOWN || mode == ROUND_HALF_EVEN) then
|
|
1193
|
+
if (half < 0) then
|
|
1194
|
+
mode = ROUND_DOWN
|
|
1195
|
+
elsif half > 0 then
|
|
1196
|
+
mode = ROUND_UP
|
|
1197
|
+
else
|
|
1198
|
+
# half == 0
|
|
1199
|
+
if (mode == ROUND_HALF_UP) then
|
|
1200
|
+
mode = ROUND_UP
|
|
1201
|
+
elsif (mode == ROUND_HALF_DOWN) then
|
|
1202
|
+
mode = ROUND_DOWN
|
|
1203
|
+
else
|
|
1204
|
+
# mode == ROUND_HALF_EVEN
|
|
1205
|
+
mode = (quot[0] == 1 ? ROUND_UP : ROUND_DOWN)
|
|
1206
|
+
end
|
|
1207
|
+
end
|
|
1208
|
+
end
|
|
1209
|
+
end
|
|
1210
|
+
|
|
1211
|
+
if mode == ROUND_UP
|
|
1212
|
+
quot += sign_quot
|
|
1213
|
+
end
|
|
1214
|
+
new_int_val = quot
|
|
1215
|
+
LongDecimal(new_int_val, new_scale)
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
def coerce(other)
|
|
1219
|
+
if other.kind_of? LongDecimal then
|
|
1220
|
+
return LongDecimalQuot(other.to_r, other.scale), self
|
|
1221
|
+
elsif other.kind_of? LongDecimalQuot then
|
|
1222
|
+
return other, self
|
|
1223
|
+
elsif other.kind_of? Rational then
|
|
1224
|
+
s = scale
|
|
1225
|
+
return LongDecimalQuot(other, s), self
|
|
1226
|
+
elsif (other.kind_of? Integer) then
|
|
1227
|
+
return LongDecimalQuot(other.to_r, scale), self
|
|
1228
|
+
elsif other.kind_of? Float then
|
|
1229
|
+
return LongDecimalQuot(other.to_ld.to_r, scale), self
|
|
1230
|
+
elsif other.kind_of? Numeric then
|
|
1231
|
+
s, o = other.coerce(self.to_f)
|
|
1232
|
+
return o, s
|
|
1233
|
+
else
|
|
1234
|
+
raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimalQuot"
|
|
1235
|
+
end
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1238
|
+
def ==(other)
|
|
1239
|
+
(other.kind_of? LongDecimalQuot) && (self <=> other) == 0 && self.scale == other.scale
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
#
|
|
1243
|
+
# Compares the two numbers.
|
|
1244
|
+
#
|
|
1245
|
+
def <=> (other)
|
|
1246
|
+
diff = (self - other)
|
|
1247
|
+
if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
|
|
1248
|
+
diff.sgn
|
|
1249
|
+
else
|
|
1250
|
+
diff <=> 0
|
|
1251
|
+
end
|
|
1252
|
+
end
|
|
1253
|
+
|
|
1254
|
+
def scale_ufo(other)
|
|
1255
|
+
raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
|
|
1256
|
+
self.scale <=> other.scale
|
|
1257
|
+
end
|
|
1258
|
+
|
|
1259
|
+
def scale_equal(other)
|
|
1260
|
+
scale_ufo(other).zero?
|
|
1261
|
+
end
|
|
1262
|
+
|
|
1263
|
+
# is self expressable as an integer without loss of digits?
|
|
1264
|
+
def is_int?
|
|
1265
|
+
denominator == 1
|
|
1266
|
+
end
|
|
1267
|
+
|
|
1268
|
+
def sgn
|
|
1269
|
+
numerator <=> 0
|
|
1270
|
+
end
|
|
1271
|
+
alias signum sgn
|
|
1272
|
+
alias sign sgn
|
|
1273
|
+
|
|
1274
|
+
#
|
|
1275
|
+
# Returns a hash code for the complex number.
|
|
1276
|
+
#
|
|
1277
|
+
def hash
|
|
1278
|
+
rat.hash ^ scale.hash
|
|
1279
|
+
end
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
def scale
|
|
1283
|
+
@scale
|
|
1284
|
+
end
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
#
|
|
1288
|
+
# Returns "<tt>LongDecimalQuot(<i>int_val</i>, <i>scale</i>, <i>num</i>, <i>denom</i>)</tt>".
|
|
1289
|
+
#
|
|
1290
|
+
def inspect
|
|
1291
|
+
sprintf("LongDecimalQuot(Rational(%s, %s), %s)", numerator.inspect, denominator.inspect, scale.inspect)
|
|
1292
|
+
end
|
|
1293
|
+
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
#
|
|
1297
|
+
# Creates a LongDecimal number. +a+ and +b+ should be Numeric.
|
|
1298
|
+
#
|
|
1299
|
+
def LongDecimal(a, b = 0)
|
|
1300
|
+
if b == 0 && (a.kind_of? LongDecimal) then
|
|
1301
|
+
a
|
|
1302
|
+
else
|
|
1303
|
+
LongDecimal.new!(a, b)
|
|
1304
|
+
end
|
|
1305
|
+
end
|
|
1306
|
+
|
|
1307
|
+
def LongDecimalQuot(first, second)
|
|
1308
|
+
LongDecimalQuot.new!(first, second)
|
|
1309
|
+
end
|
|
1310
|
+
|
|
1311
|
+
class Numeric
|
|
1312
|
+
|
|
1313
|
+
def to_ld
|
|
1314
|
+
LongDecimal(self)
|
|
1315
|
+
end
|
|
1316
|
+
|
|
1317
|
+
end
|
|
1318
|
+
|
|
1319
|
+
# end of file longdecimal.rb
|