long-decimal 0.00.13 → 0.00.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. data/README +16 -10
  2. data/VERSION +1 -1
  3. data/doc/classes/LongMath.src/M000019.html +2 -2
  4. data/doc/classes/LongMath.src/M000020.html +2 -1
  5. data/doc/classes/LongMath.src/M000021.html +1 -1
  6. data/doc/classes/LongMath.src/M000022.html +1 -1
  7. data/doc/classes/LongMath.src/M000023.html +1 -1
  8. data/doc/classes/LongMath.src/M000024.html +1 -1
  9. data/doc/classes/LongMath.src/M000025.html +1 -1
  10. data/doc/classes/LongMath/LongDecimal.src/M000060.html +1 -1
  11. data/doc/classes/LongMath/LongDecimal.src/M000061.html +1 -1
  12. data/doc/classes/LongMath/LongDecimal.src/M000062.html +2 -2
  13. data/doc/classes/LongMath/LongDecimal.src/M000063.html +1 -1
  14. data/doc/classes/LongMath/LongDecimal.src/M000064.html +1 -1
  15. data/doc/classes/LongMath/LongDecimal.src/M000065.html +1 -1
  16. data/doc/classes/LongMath/LongDecimal.src/M000066.html +1 -1
  17. data/doc/classes/LongMath/LongDecimal.src/M000067.html +1 -1
  18. data/doc/classes/LongMath/LongDecimal.src/M000068.html +1 -1
  19. data/doc/classes/LongMath/LongDecimal.src/M000069.html +1 -1
  20. data/doc/classes/LongMath/LongDecimal.src/M000070.html +3 -3
  21. data/doc/classes/LongMath/LongDecimal.src/M000071.html +1 -1
  22. data/doc/classes/LongMath/LongDecimal.src/M000072.html +1 -1
  23. data/doc/classes/LongMath/LongDecimal.src/M000073.html +1 -1
  24. data/doc/classes/LongMath/LongDecimal.src/M000074.html +1 -1
  25. data/doc/classes/LongMath/LongDecimal.src/M000075.html +1 -1
  26. data/doc/classes/LongMath/LongDecimal.src/M000076.html +1 -1
  27. data/doc/classes/LongMath/LongDecimal.src/M000077.html +1 -1
  28. data/doc/classes/LongMath/LongDecimal.src/M000078.html +1 -1
  29. data/doc/classes/LongMath/LongDecimal.src/M000079.html +2 -2
  30. data/doc/classes/LongMath/LongDecimal.src/M000080.html +2 -2
  31. data/doc/classes/LongMath/LongDecimal.src/M000081.html +1 -1
  32. data/doc/classes/LongMath/LongDecimal.src/M000082.html +1 -1
  33. data/doc/classes/LongMath/LongDecimal.src/M000083.html +1 -1
  34. data/doc/classes/LongMath/LongDecimal.src/M000085.html +1 -1
  35. data/doc/classes/LongMath/LongDecimal.src/M000086.html +1 -1
  36. data/doc/classes/LongMath/LongDecimal.src/M000087.html +1 -1
  37. data/doc/classes/LongMath/LongDecimal.src/M000088.html +1 -1
  38. data/doc/classes/LongMath/LongDecimal.src/M000089.html +1 -1
  39. data/doc/classes/LongMath/LongDecimal.src/M000090.html +1 -1
  40. data/doc/classes/LongMath/LongDecimal.src/M000091.html +1 -1
  41. data/doc/classes/LongMath/LongDecimal.src/M000092.html +1 -1
  42. data/doc/classes/LongMath/LongDecimal.src/M000093.html +1 -1
  43. data/doc/classes/LongMath/LongDecimal.src/M000094.html +1 -1
  44. data/doc/classes/LongMath/LongDecimal.src/M000095.html +1 -1
  45. data/doc/classes/LongMath/LongDecimal.src/M000096.html +1 -1
  46. data/doc/classes/LongMath/LongDecimal.src/M000097.html +1 -1
  47. data/doc/classes/LongMath/LongDecimal.src/M000098.html +1 -1
  48. data/doc/classes/LongMath/LongDecimal.src/M000099.html +1 -1
  49. data/doc/classes/LongMath/LongDecimal.src/M000100.html +1 -1
  50. data/doc/classes/LongMath/LongDecimal.src/M000101.html +1 -1
  51. data/doc/classes/LongMath/LongDecimal.src/M000102.html +1 -1
  52. data/doc/classes/LongMath/LongDecimal.src/M000103.html +1 -1
  53. data/doc/classes/LongMath/LongDecimal.src/M000104.html +1 -1
  54. data/doc/classes/LongMath/LongDecimal.src/M000105.html +1 -1
  55. data/doc/classes/LongMath/LongDecimal.src/M000106.html +1 -1
  56. data/doc/classes/LongMath/LongDecimal.src/M000107.html +1 -1
  57. data/doc/classes/LongMath/LongDecimal.src/M000108.html +1 -1
  58. data/doc/classes/LongMath/LongDecimal.src/M000109.html +1 -1
  59. data/doc/classes/LongMath/LongDecimal.src/M000110.html +1 -1
  60. data/doc/classes/LongMath/LongDecimal.src/M000111.html +1 -1
  61. data/doc/classes/LongMath/LongDecimal.src/M000112.html +1 -1
  62. data/doc/classes/LongMath/LongDecimal.src/M000113.html +1 -1
  63. data/doc/classes/LongMath/LongDecimal.src/M000114.html +1 -1
  64. data/doc/classes/LongMath/LongDecimal.src/M000115.html +1 -1
  65. data/doc/classes/LongMath/LongDecimal.src/M000116.html +1 -1
  66. data/doc/classes/LongMath/LongDecimal.src/M000117.html +1 -1
  67. data/doc/classes/LongMath/LongDecimal.src/M000119.html +1 -1
  68. data/doc/classes/LongMath/LongDecimal.src/M000121.html +1 -1
  69. data/doc/classes/LongMath/LongDecimal.src/M000122.html +1 -1
  70. data/doc/classes/LongMath/LongDecimal.src/M000123.html +1 -1
  71. data/doc/classes/LongMath/LongDecimal.src/M000124.html +1 -1
  72. data/doc/classes/LongMath/LongDecimal.src/M000125.html +1 -1
  73. data/doc/classes/LongMath/LongDecimal.src/M000126.html +1 -1
  74. data/doc/classes/LongMath/LongDecimal.src/M000129.html +1 -1
  75. data/doc/classes/LongMath/LongDecimal.src/M000130.html +1 -1
  76. data/doc/classes/LongMath/LongDecimal.src/M000131.html +1 -1
  77. data/doc/classes/LongMath/LongDecimal.src/M000132.html +1 -1
  78. data/doc/classes/LongMath/LongDecimal.src/M000133.html +1 -1
  79. data/doc/classes/LongMath/LongDecimalQuot.src/M000026.html +1 -1
  80. data/doc/classes/LongMath/LongDecimalQuot.src/M000027.html +1 -1
  81. data/doc/classes/LongMath/LongDecimalQuot.src/M000028.html +1 -1
  82. data/doc/classes/LongMath/LongDecimalQuot.src/M000029.html +1 -1
  83. data/doc/classes/LongMath/LongDecimalQuot.src/M000030.html +1 -1
  84. data/doc/classes/LongMath/LongDecimalQuot.src/M000031.html +1 -1
  85. data/doc/classes/LongMath/LongDecimalQuot.src/M000032.html +1 -1
  86. data/doc/classes/LongMath/LongDecimalQuot.src/M000033.html +1 -1
  87. data/doc/classes/LongMath/LongDecimalQuot.src/M000034.html +1 -1
  88. data/doc/classes/LongMath/LongDecimalQuot.src/M000035.html +1 -1
  89. data/doc/classes/LongMath/LongDecimalQuot.src/M000036.html +1 -1
  90. data/doc/classes/LongMath/LongDecimalQuot.src/M000037.html +1 -1
  91. data/doc/classes/LongMath/LongDecimalQuot.src/M000038.html +1 -1
  92. data/doc/classes/LongMath/LongDecimalQuot.src/M000039.html +1 -1
  93. data/doc/classes/LongMath/LongDecimalQuot.src/M000040.html +1 -1
  94. data/doc/classes/LongMath/LongDecimalQuot.src/M000041.html +1 -1
  95. data/doc/classes/LongMath/LongDecimalQuot.src/M000042.html +1 -1
  96. data/doc/classes/LongMath/LongDecimalQuot.src/M000043.html +1 -1
  97. data/doc/classes/LongMath/LongDecimalQuot.src/M000044.html +1 -1
  98. data/doc/classes/LongMath/LongDecimalQuot.src/M000045.html +1 -1
  99. data/doc/classes/LongMath/LongDecimalQuot.src/M000046.html +1 -1
  100. data/doc/classes/LongMath/LongDecimalQuot.src/M000047.html +1 -1
  101. data/doc/classes/LongMath/LongDecimalQuot.src/M000048.html +1 -1
  102. data/doc/classes/LongMath/LongDecimalQuot.src/M000049.html +1 -1
  103. data/doc/classes/LongMath/LongDecimalQuot.src/M000050.html +1 -1
  104. data/doc/classes/LongMath/LongDecimalQuot.src/M000051.html +1 -1
  105. data/doc/classes/LongMath/LongDecimalQuot.src/M000052.html +1 -1
  106. data/doc/classes/LongMath/LongDecimalQuot.src/M000053.html +1 -1
  107. data/doc/classes/LongMath/LongDecimalQuot.src/M000054.html +1 -1
  108. data/doc/classes/LongMath/LongDecimalQuot.src/M000055.html +1 -1
  109. data/doc/classes/LongMath/LongDecimalQuot.src/M000058.html +1 -1
  110. data/doc/classes/LongMath/LongDecimalQuot.src/M000059.html +1 -1
  111. data/doc/classes/LongMath/Numeric.src/M000134.html +1 -1
  112. data/doc/created.rid +1 -1
  113. data/doc/files/lib/long-decimal_rb.html +2 -2
  114. data/lib/long-decimal.rb +14 -29
  115. data/test/testlongdecimal.rb +371 -15
  116. metadata +2 -3
  117. data/lib/longdecimal.rb +0 -2246
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: long-decimal
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.00.13
7
- date: 2006-03-18 00:00:00 +01:00
6
+ version: 0.00.14
7
+ date: 2006-03-19 00:00:00 +01:00
8
8
  summary: LongDecimal for numbers with fixed point
9
9
  require_paths:
10
10
  - lib
@@ -195,7 +195,6 @@ files:
195
195
  - doc/classes/LongDecimalRoundingMode/RoundingModeClass.src
196
196
  - doc/classes/LongDecimalRoundingMode/RoundingModeClass.html
197
197
  - doc/classes/LongDecimalRoundingMode/RoundingModeClass.src/M000135.html
198
- - lib/longdecimal.rb
199
198
  - lib/long-decimal.rb
200
199
  - test/testlongdecimal.rb
201
200
  test_files:
data/lib/longdecimal.rb DELETED
@@ -1,2246 +0,0 @@
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.9 2006/03/18 06:03:58 bk1 Exp $
5
- # CVS-Label: $Name: PRE_ALPHA_0_13 $
6
- # Author: $Author: bk1 $ (Karl Brodowsky)
7
- #
8
- require "complex"
9
- require "rational"
10
- require "bigdecimal"
11
-
12
- # require "bigdecimal/math"
13
-
14
- #
15
- # define rounding modes to be used for LongDecimal
16
- # this serves the purpose of an "enum" in C/C++
17
- #
18
- module LongDecimalRoundingMode
19
-
20
- RoundingModeClass = Struct.new(:name, :num)
21
-
22
- #
23
- # enumeration class to express the possible rounding modes that are
24
- # supported by LongDecimal
25
- #
26
- class RoundingModeClass
27
- include Comparable
28
-
29
- #
30
- # introduce some ordering for rounding modes
31
- #
32
- def <=>(o)
33
- if o.respond_to?:num
34
- self.num <=> o.num
35
- else
36
- self.num <=> o
37
- end
38
- end
39
- end
40
-
41
- #
42
- # rounding modes as constants
43
- #
44
- ROUND_UP = RoundingModeClass.new(:ROUND_UP, 0)
45
- ROUND_DOWN = RoundingModeClass.new(:ROUND_DOWN, 1)
46
- ROUND_CEILING = RoundingModeClass.new(:ROUND_CEILING, 2)
47
- ROUND_FLOOR = RoundingModeClass.new(:ROUND_FLOOR, 3)
48
- ROUND_HALF_UP = RoundingModeClass.new(:ROUND_HALF_UP, 4)
49
- ROUND_HALF_DOWN = RoundingModeClass.new(:ROUND_HALF_DOWN, 5)
50
- ROUND_HALF_EVEN = RoundingModeClass.new(:ROUND_HALF_EVEN, 6)
51
- ROUND_UNNECESSARY = RoundingModeClass.new(:ROUND_UNNECESSARY, 7)
52
-
53
- end # LongDecimalRoundingMode
54
-
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.
61
- #
62
- module LongMath
63
-
64
- include LongDecimalRoundingMode
65
-
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)
70
-
71
- #
72
- # helper method for internal use: checks if word_len is a reasonable
73
- # size for splitting a number into parts
74
- #
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
78
- end
79
-
80
- #
81
- # helper method for internal use: checks if parameter x is an Integer
82
- #
83
- def LongMath.check_is_int(x, name="x")
84
- raise TypeError, "#{name}=#{x.inspect} must be Integer" unless x.kind_of? Integer
85
- end
86
-
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
-
94
- #
95
- # helper method for internal use: checks if parameter x is a
96
- # reasonable value for the precision (scale) of a LongDecimal
97
- #
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
101
- end
102
-
103
- #
104
- # helper method for internal use: checks if parameter x is a
105
- # rounding mode (instance of RoundingModeClass)
106
- #
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
109
- end
110
-
111
- #
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)
115
- #
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
132
- end
133
-
134
- #
135
- # concatenate numbers given in words as bit patterns
136
- # (the opposite of split_to_words)
137
- #
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
154
- end
155
-
156
- #
157
-
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
- #
162
- def LongMath.sqrtb(x)
163
- a = sqrtb_with_remainder(x)
164
- a[0]
165
- end
166
-
167
- #
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.
172
- #
173
- def LongMath.sqrtb_with_remainder(x)
174
- check_is_int(x, "x")
175
-
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
183
-
184
- xwords = split_to_words(x, 2)
185
- xi = xwords[0] - 1
186
- yi = 1
187
-
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
201
-
202
- #
203
-
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
216
-
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")
231
-
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
239
-
240
- xwords = split_to_words(x, n2)
241
- if (xwords.length == 1) then
242
- return sqrtb_with_remainder(xwords[0])
243
- end
244
-
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
252
-
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
277
- end
278
- xi = r
279
- yi = (yi << n) + q
280
- end
281
- return [ yi, xi ]
282
- end
283
-
284
- #
285
-
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
300
- 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
310
-
311
- #
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
318
- #
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
325
-
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
330
- else
331
- return m1
332
- end
333
-
334
- elsif (x.kind_of? Integer)
335
-
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
339
- 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
346
- end
347
- result += (Math.log(power) / Math.log(prime_number)).round
348
- end
349
- return result
350
- else
351
- raise TypeError, "type of x is not supported #{x.class} #{x.inpect}"
352
- end
353
- end
354
-
355
- #
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
381
-
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
389
- 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")
395
- end
396
- pi.round_to_scale(prec, final_mode)
397
- end
398
-
399
- #
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.
406
- #
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)
413
- end
414
-
415
- #
416
- # private helper method for exponentiation
417
- # calculate internal precision
418
- #
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
428
- end
429
- # puts("calc_iprec_for_exp: x=#{x} prec=#{prec} iprec=#{iprec} iprec_extra=#{iprec_extra}\n")
430
- iprec
431
- end
432
-
433
- # private :calc_iprec_for_exp
434
-
435
- #
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
441
- #
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
489
-
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)
515
- 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")
523
- end
524
- y = y_k.round_to_scale(prec, final_mode)
525
- y
526
- end
527
-
528
- #
529
- # calculate the natural logarithm function of x to the given precision as
530
- # LongDecimal.
531
- #
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)
537
- end
538
-
539
- #
540
- # calculate the base 10 logarithm of x to the given precision as
541
- # LongDecimal.
542
- #
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).to_ld
557
- return y.round_to_scale(prec, mode)
558
- end
559
-
560
- #
561
- # calculate the base 2 logarithm of x to the given precision as
562
- # LongDecimal.
563
- #
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).to_ld
578
- end
579
-
580
- #
581
- # internal functionality of log. exposes some more parameters, that
582
- # should usually be set to defaut values, in order to allow better testing.
583
- # do not actually call this method unless you are testing log.
584
- # create a bug report, if the default settings for the parameters do
585
- # not work correctly
586
- #
587
- def LongMath.log_internal(x, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
588
- check_is_ld(x)
589
- raise TypeError, "x=#{x.inspect} must not be positive" unless x > 0
590
- if (prec == nil) then
591
- prec = x.scale
592
- end
593
- check_is_prec(prec, "prec")
594
- if (x.one?) then
595
- return LongDecimal.zero!(prec)
596
- end
597
-
598
- if (final_mode == nil)
599
- final_mode = LongDecimal::ROUND_HALF_DOWN
600
- end
601
- check_is_mode(final_mode, "final_mode")
602
- check_is_mode(mode, "mode")
603
-
604
- if (iprec == nil) then
605
- iprec = ((prec+10)*1.20).round
606
- end
607
- if (iprec < prec) then
608
- iprec = prec
609
- end
610
- check_is_prec(iprec, "iprec")
611
-
612
- # dprec = [ iprec - 1, (prec + 1) << 1 ].min
613
- dprec = iprec - 1
614
-
615
- y = 0
616
- s = 1
617
- if (x < 1) then
618
- # puts("x=#{x} iprec=#{iprec}\n")
619
- x = (1 / x).round_to_scale(iprec, mode)
620
- s = -1
621
- # puts("s=#{s} x=#{x} iprec=#{iprec}\n")
622
- end
623
- exp_part = 0
624
- estimate = 0
625
- while (x > MAX_FLOATABLE) do
626
- if (exp_part == 0) then
627
- estimate = MAX_EXP_ABLE.to_ld
628
- exp_part = exp(estimate, iprec)
629
- end
630
- x = (x / exp_part).round_to_scale(iprec, mode)
631
- if (s < 0) then
632
- y -= estimate
633
- else
634
- y += estimate
635
- end
636
- end
637
-
638
- delta = LongDecimal(1, 3)
639
- while (x - 1).abs > delta do
640
- # puts("too far from 1: x=#{x}\n")
641
- xf = x.to_f
642
- # puts("xf=#{xf}\n")
643
- mlx = Math.log(xf)
644
- # puts("log(xf)=#{mlx}\n")
645
- estimate = mlx.to_ld.round_to_scale(20, mode)
646
- exp_part = exp(estimate, iprec << 1)
647
- # puts("y=#{y} s=#{s} est=#{estimate} part=#{exp_part} x=#{x}\n")
648
- x = (x / exp_part).round_to_scale(iprec, mode)
649
- # puts("divided by exp_part=#{exp_part}: #{x}\n")
650
- if (s < 0) then
651
- y -= estimate
652
- else
653
- y += estimate
654
- end
655
- # puts("y=#{y} s=#{s} est=#{estimate} part=#{exp_part} x=#{x}\n")
656
- end
657
-
658
- factor = 1
659
- # delta = LongDecimal(1, (iprec.to_f**(1/3)).round)
660
- # while (x - 1).abs > delta do
661
- # x = sqrt(x)
662
- # factor *= 2
663
- # end
664
-
665
- sum = 0
666
- z = 1 - x
667
- i = 1
668
- p = 1.to_ld
669
- d = 1.to_ld
670
- until p.abs.round_to_scale(dprec, LongDecimal::ROUND_DOWN).zero? do
671
- p = (p * z).round_to_scale(iprec, mode)
672
- d = (p / i).round_to_scale(iprec, mode)
673
- i += 1
674
- sum += d
675
-
676
- # puts("log_internal: s=#{sum} d=#{d} x=#{x} i=#{i} p=#{p} iprec=#{iprec} dprec=#{dprec}\n") if (i & 0x0f == 0x0f)
677
- end
678
-
679
- # puts("y=#{y} s=#{s} f=#{factor} sum=#{sum}\n")
680
- y -= ((s * factor) * sum).round_to_scale(iprec, mode)
681
- # puts("y=#{y} s=#{s} f=#{factor} sum=#{sum}\n")
682
- return y.round_to_scale(prec, final_mode)
683
-
684
- end
685
-
686
- #
687
- # calc the power of x with exponent y to the given precision as
688
- # LongDecimal. Only supports values of y such that exp(y) still
689
- # fits into a float (y <= 709)
690
- #
691
- def LongMath.power(x, y, prec, mode = LongDecimal::ROUND_HALF_DOWN)
692
- check_is_ld(x, "x")
693
- check_is_ld(y, "y")
694
- raise TypeError, "y=#{y.inspect} must not be greater #{MAX_EXP_ABLE}" unless y <= MAX_EXP_ABLE
695
- raise TypeError, "x=#{x.inspect} must not be greater #{MAX_FLOATABLE}" unless x <= MAX_FLOATABLE
696
- raise TypeError, "x=#{x.inspect} must not positive" unless x > 0
697
- check_is_prec(prec, "prec")
698
- check_is_mode(mode, "mode")
699
- LongMath.power_internal(x, y, prec, mode)
700
- end
701
-
702
- #
703
- # internal functionality of exp. exposes some more parameters, that
704
- # should usually be set to defaut values, in order to allow better testing.
705
- # do not actually call this method unless you are testing exp.
706
- # create a bug report, if the default settings for the parameters do
707
- # not work correctly
708
- #
709
- def LongMath.power_internal(x, y, prec = nil, final_mode = LongDecimal::ROUND_HALF_DOWN, iprec = nil, mode = LongDecimal::ROUND_HALF_DOWN)
710
- check_is_ld(x, "x")
711
- if (prec == nil) then
712
- prec = x.scale
713
- end
714
- check_is_prec(prec, "prec")
715
-
716
- if (final_mode == nil)
717
- final_mode = LongDecimal::ROUND_HALF_DOWN
718
- end
719
- check_is_mode(final_mode, "final_mode")
720
- check_is_mode(mode, "mode")
721
-
722
- logx_y_f = Math.log(x.to_f) * (y.to_f)
723
-
724
- # iprec = (prec * 1.2 + 20 + (y.abs.to_f) * 1.5 * x.int_digits2).round
725
- if (iprec == nil) then
726
- iprec = calc_iprec_for_exp(logx_y_f, prec) + 2
727
- end
728
- # puts("power_internal: x=#{x} y=#{y} logx_y=#{logx_y_f} iprec=#{iprec} prec=#{prec}\n")
729
- logx = log(x, iprec, mode)
730
- logx_y = logx*y
731
- xy = exp_internal(logx_y, prec + 1, mode)
732
- # puts("power_internal: x=#{x} logx=#{logx} y=#{y} logx_y=#{logx_y} xy=#{xy} iprec=#{iprec} prec=#{prec}\n")
733
- xy.round_to_scale(prec, final_mode)
734
- end
735
-
736
- end # LongMath
737
-
738
- #
739
- # class for holding fixed point long decimal numbers
740
- # these can be considered as a pair of two integer. One contains the
741
- # digits and the other one the position of the decimal point.
742
- #
743
- class LongDecimal < Numeric
744
- @RCS_ID='-$Id: longdecimal.rb,v 1.9 2006/03/18 06:03:58 bk1 Exp $-'
745
-
746
- include LongDecimalRoundingMode
747
-
748
- # MINUS_ONE = LongDecimal(-1)
749
- # ZERO = LongDecimal(0)
750
- # ONE = LongDecimal(1)
751
- # TWO = LongDecimal(2)
752
- # TEN = LongDecimal(10)
753
-
754
- #
755
- # initialization
756
- # parameters:
757
- # 1. LongDecimal.new!(x) where x is a string or a number
758
- # the resulting LongDecimal holds the number x, possibly rounded
759
- # 2. LongDecimal.new!(x, s) where x is a string or a number and s is the scale
760
- # the resulting LongDecimal holds the number x / 10**s
761
- #
762
- def LongDecimal.new!(x, s = 0)
763
- new(x, s)
764
- end
765
-
766
- #
767
- # creates a LongDecimal representing zero with the given number of
768
- # digits after the decimal point (scale=s)
769
- #
770
- def LongDecimal.zero!(s = 0)
771
- new(0, s)
772
- end
773
-
774
-
775
- #
776
- # creates a LongDecimal representing one with the given number of
777
- # digits after the decimal point (scale=s)
778
- #
779
- def LongDecimal.one!(s = 0)
780
- new(1, s)
781
- end
782
-
783
-
784
- #
785
- # creates a LongDecimal representing two with the given number of
786
- # digits after the decimal point (scale=s)
787
- #
788
- def LongDecimal.two!(s = 0)
789
- new(2*10**s, s)
790
- end
791
-
792
-
793
- #
794
- # creates a LongDecimal representing ten with the given number of
795
- # digits after the decimal point (scale=s)
796
- #
797
- def LongDecimal.ten!(s = 0)
798
- new(10**(s+1), s)
799
- end
800
-
801
-
802
- #
803
- # creates a LongDecimal representing minus one with the given number of
804
- # digits after the decimal point (scale=s)
805
- #
806
- def LongDecimal.minus_one!(s = 0)
807
- new(-1*10**s, s)
808
- end
809
-
810
-
811
- #
812
- # creates a LongDecimal representing a power of ten with the given
813
- # exponent e and with the given number of digits after the decimal
814
- # point (scale=s)
815
- #
816
- def LongDecimal.power_of_ten!(e, s = 0)
817
- LongMath.check_is_int(e, "e")
818
- raise TypeError, "negative 1st arg \"#{e.inspect}\"" if e < 0
819
- new(10**(s+e), s)
820
- end
821
-
822
-
823
- #
824
- # initialization
825
- # parameters:
826
- # LongDecimal.new(x, s) where x is a string or a number and s is the scale
827
- # the resulting LongDecimal holds the number x / 10**s
828
- #
829
- def initialize(x, s)
830
-
831
- # handle some obvious errors with x first
832
- raise TypeError, "non numeric 1st arg \"#{x.inspect}\"" if ! (x.kind_of? Numeric) && ! (x.kind_of? String)
833
- # we could maybe even work with complex number, if their imaginary part is zero.
834
- # but this is not so important to deal with, so we raise an error anyway.
835
- raise TypeError, "complex numbers not supported \"#{x.inspect}\"" if x.kind_of? Complex
836
-
837
- # handle some obvious errors with optional second parameter, if present
838
- raise TypeError, "non integer 2nd arg \"#{s.inspect}\"" if ! s.kind_of? Integer
839
- raise TypeError, "negative 2nd arg \"#{s.inspect}\"" if s < 0
840
-
841
- # scale is the second parameter or 0 if it is missing
842
- scale = s
843
- # int_val is the integral value that is multiplied by some 10**-n
844
- int_val = 0
845
-
846
- if x.kind_of? Integer then
847
- # integers are trivial to handle
848
- int_val = x
849
-
850
- elsif x.kind_of? Rational then
851
- # rationals are rounded somehow
852
- # we need to come up with a better rule here.
853
- # if denominator is any product of powers of 2 and 5, we do not need to round
854
- denom = x.denominator
855
- mul_2 = LongMath.multiplicity_of_factor(denom, 2)
856
- mul_5 = LongMath.multiplicity_of_factor(denom, 5)
857
- iscale = [mul_2, mul_5].max
858
- scale += iscale
859
- denom /= 2 ** mul_2
860
- denom /= 5 ** mul_5
861
- iscale2 = Math.log10(denom).ceil
862
- scale += iscale2
863
- # int_val = (x * 10 ** scale).to_i
864
- int_val = (x * 10 ** (iscale2+iscale)).to_i
865
-
866
- else
867
- # we assume a string or a floating point number
868
- # floating point number or BigDecimal is converted to string, so
869
- # we only deal with strings
870
- # this operation is not so common, so there is no urgent need to
871
- # optimize it
872
- num_str = x.to_s
873
- len = num_str.length
874
-
875
- # handle the obvious error that string is empty
876
- raise TypeError, "1st arg must not be empty string. \"#{num_str.inspect}\"" if len == 0
877
-
878
- # remove spaces and underscores
879
- num_str.gsub! /\s/, ""
880
- num_str.gsub! /_/, ""
881
-
882
- # handle sign
883
- num_str.gsub! /^\+/, ""
884
- negative = false
885
- if num_str.gsub! /^-/, "" then
886
- negative = true
887
- end
888
-
889
- # split in parts before and after decimal point
890
- num_arr = num_str.split /\./
891
- if num_arr.length > 2 then
892
- raise TypeError, "1st arg contains more than one . \"#{num_str.inspect}\""
893
- end
894
- num_int = num_arr[0]
895
- num_rem = num_arr[1]
896
- num_frac = nil
897
- num_exp = nil
898
- unless num_rem.nil? then
899
- num_arr = num_rem.split /[Ee]/
900
- num_frac = num_arr[0]
901
- num_exp = num_arr[1]
902
- end
903
-
904
- if num_frac.nil? then
905
- num_frac = ""
906
- end
907
-
908
- if num_exp.nil? || num_exp.empty? then
909
- num_exp = "0"
910
- end
911
- num_exp = num_exp.to_i
912
- iscale = num_frac.length - num_exp
913
- scale += iscale
914
- int_val = (num_int + num_frac).to_i
915
- if negative then
916
- int_val = -int_val
917
- end
918
- end
919
- @scale = scale
920
- @int_val = int_val
921
-
922
- end # initialize
923
-
924
- attr_reader :int_val, :scale
925
-
926
- # #
927
- # # get the integer value of self, disregarding the decimal point.
928
- # # Mostly for internal use.
929
- # #
930
- # def int_val
931
- # @int_val
932
- # end
933
-
934
- # #
935
- # # get the scale, i.e. the position of the decimal point.
936
- # # Mostly for internal use.
937
- # #
938
- # def scale
939
- # @scale
940
- # end
941
-
942
- #
943
- # alter scale (changes self)
944
- #
945
- # only for internal use:
946
- # use round_to_scale instead
947
- #
948
- def scale=(s)
949
- raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
950
- raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
951
-
952
- # do not work too hard, if scale does not really change.
953
- unless @scale == s then
954
- # multiply int_val by a power of 10 in order to compensate for
955
- # the change of scale and to keep number in the same order of magnitude.
956
- d = s - @scale
957
- f = 10 ** (d.abs)
958
- if (d >= 0) then
959
- @int_val = (@int_val * f).to_i
960
- else
961
- # here we actually do rounding
962
- @int_val = (@int_val / f).to_i
963
- end
964
- @scale = s
965
- end
966
- end
967
-
968
- protected :scale=
969
-
970
- #
971
- # create copy of self with different scale
972
- # param1: new_scale new scale for result
973
- # param2: mode rounding mode to be applied when information is
974
- # lost. defaults to ROUND_UNNECESSARY, which
975
- # means that an exception is thrown if rounding
976
- # would actually loose any information.
977
- #
978
- def round_to_scale(new_scale, mode = ROUND_UNNECESSARY)
979
-
980
- raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
981
- raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
982
- raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
983
- if @scale == new_scale then
984
- self
985
- else
986
- diff = new_scale - scale
987
- factor = 10 ** (diff.abs)
988
- if (diff > 0) then
989
- # we become more precise, no rounding issues
990
- new_int_val = int_val * factor
991
- else
992
- quot, rem = int_val.divmod(factor)
993
- if (rem == 0) then
994
- new_int_val = quot
995
- elsif (mode == ROUND_UNNECESSARY) then
996
- raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
997
- else
998
- return LongDecimalQuot(self, LongDecimal(1)).round_to_scale(new_scale, mode)
999
- end
1000
- end
1001
- LongDecimal(new_int_val, new_scale)
1002
- end
1003
- end
1004
-
1005
- #
1006
- # convert self into String, which is the decimal representation.
1007
- # Use trailing zeros, if int_val has them.
1008
-
1009
- # optional parameter shown_scale is the number of digits after the
1010
- # decimal point. Defaults to the scale of self.
1011
- # optional parameter mode ist the rounding mode to be applied.
1012
- # Defaults to ROUND_UNNECESSARY, in which case an exception is
1013
- # thrown if rounding is actually necessary.
1014
- # optional parameter base is the base to be used when expressing
1015
- # self as string. defaults to 10.
1016
- #
1017
- def to_s(shown_scale = @scale, mode = ROUND_UNNECESSARY, base = 10)
1018
- if (base == 10) then
1019
- if (shown_scale == @scale)
1020
- to_s_10
1021
- else
1022
- s = self.round_to_scale(shown_scale, mode)
1023
- s.to_s_10
1024
- end
1025
- else
1026
- # base is not 10
1027
- unless (base.kind_of? Integer) && 2 <= base && base <= 36 then
1028
- raise TypeError, "base must be integer between 2 and 36"
1029
- end
1030
- quot = (self.move_point_right(scale) * base ** shown_scale) / 10 ** scale
1031
- p(quot)
1032
- rounded = quot.round_to_scale(0, mode)
1033
- p(rounded)
1034
- rounded.to_s_internal(base, shown_scale)
1035
- end
1036
- end
1037
-
1038
- #
1039
- # internal helper method, converts self to string in decimal system
1040
- # with default settings.
1041
- #
1042
- def to_s_10
1043
- to_s_internal(10, scale)
1044
- end
1045
-
1046
- #
1047
- # internal helper method, converts self to string in any number system
1048
- #
1049
- def to_s_internal(b, sc)
1050
- sg = sgn
1051
- i = int_val.abs
1052
- str = i.to_s(b)
1053
- if sc > 0 then
1054
- missing = sc - str.length + 1
1055
- if missing > 0 then
1056
- str = ("0" * missing) + str
1057
- end
1058
- str[-sc, 0] = "."
1059
- end
1060
- str = "-" + str if sg < 0
1061
- str
1062
- end
1063
-
1064
- protected :to_s_10
1065
- protected :to_s_internal
1066
-
1067
- #
1068
- # convert self into Rational
1069
- # this works quite straitforward. use int_val as numerator and a
1070
- # power of 10 as denominator
1071
- #
1072
- def to_r
1073
- Rational(numerator, denominator)
1074
- end
1075
-
1076
- #
1077
- # convert self into Float
1078
- # this works straitforward by dividing int_val by power of 10 in
1079
- # float-arithmetic, in all cases where numerator and denominator are
1080
- # within the ranges expressable as Floats. Goes via string
1081
- # representation otherwise.
1082
- #
1083
- def to_f
1084
- divisor = denominator
1085
- if (divisor == 1) then
1086
- return numerator.to_f
1087
- elsif int_val.abs <= LongMath::MAX_FLOATABLE then
1088
- if (divisor.abs > LongMath::MAX_FLOATABLE) then
1089
- return 0.0
1090
- else
1091
- f = int_val.to_f
1092
- return f / divisor
1093
- end
1094
- elsif numerator.abs < divisor
1095
- # self is between -1 and 1
1096
- # factor = numerator.abs.div(LongMath::MAX_FLOATABLE)
1097
- # digits = factor.to_ld.int_digits10
1098
- # return LongDecimal(numerator.div(10**digits), scale -digits).to_f
1099
- return self.to_s.to_f
1100
- else
1101
- # s2 = [scale.div(2), 1].max
1102
- # return LongDecimal(numerator.div(10**s2), scale - s2).to_f
1103
- return self.to_s.to_f
1104
- end
1105
- end
1106
-
1107
- #
1108
- # convert self into Integer
1109
- # This may loose information. In most cases it is preferred to
1110
- # control this by calling round_to_scale first and then applying
1111
- # to_i when the number represented by self is actually an integer.
1112
- #
1113
- def to_i
1114
- numerator.div(denominator)
1115
- end
1116
-
1117
- #
1118
- # convert self into LongDecimal (returns self)
1119
- #
1120
- def to_ld
1121
- self
1122
- end
1123
-
1124
- #
1125
- # convert selt into BigDecimal
1126
- #
1127
- def to_bd
1128
- # this operation is probably not used so heavily, so we can live with a
1129
- # string as an intermediate step.
1130
- BigDecimal(self.to_s)
1131
- end
1132
-
1133
- #
1134
- # LongDecimals can be seen as a fraction with a power of 10 as
1135
- # denominator for compatibility with other numeric classes this
1136
- # method is included, returning 10**scale.
1137
- # Please observe that there may be common factors of numerator and
1138
- # denominator in case of LongDecimal, which does not occur in case
1139
- # of Rational
1140
- #
1141
- def denominator
1142
- 10**scale
1143
- end
1144
-
1145
- #
1146
- # LongDecimals can be seen as a fraction with its int_val as its
1147
- # numerator
1148
- # Please observe that there may be common factors of numerator and
1149
- # denominator in case of LongDecimal, which does not occur in case
1150
- # of Rational
1151
- #
1152
- alias numerator int_val
1153
-
1154
- #
1155
- # number of binary digits before the decimal point, not counting a single 0.
1156
- # 0.xxx -> 0
1157
- # 1.xxx -> 1
1158
- # 2.xxx -> 2
1159
- # 4.xxx -> 3
1160
- # 8.xxx -> 4
1161
- # ...
1162
- #
1163
- def int_digits2
1164
- int_part = self.to_i.abs
1165
- if int_part.zero? then
1166
- return 0
1167
- end
1168
-
1169
- n = int_part.size * 8 - 31
1170
- int_part = int_part >> n
1171
- until int_part.zero? do
1172
- int_part = int_part >> 1
1173
- n += 1
1174
- end
1175
- n
1176
- end
1177
-
1178
- #
1179
- # number of decimal digits before the decimal point, not counting a
1180
- # single 0.
1181
- # 0.xxx -> 0
1182
- # 1.xxx -> 1
1183
- # 10.xxx -> 2
1184
- # ...
1185
- #
1186
- def int_digits10
1187
- int_part = self.to_i.abs
1188
- if int_part.zero? then
1189
- return 0
1190
- end
1191
-
1192
- id = 1
1193
- powers = []
1194
- power = 10
1195
- idx = 0
1196
- until int_part.zero? do
1197
- expon = 1 << idx
1198
- powers[idx] = power
1199
- break if int_part < power
1200
- id += expon
1201
- int_part = (int_part / power).to_i
1202
- idx += 1
1203
- power = power * power
1204
- end
1205
- until int_part < 10 do
1206
- idx -= 1
1207
- expon = 1 << idx
1208
- power = powers[idx]
1209
- # puts("i=#{int_part} p=#{power}\n")
1210
- while int_part >= power
1211
- id += expon
1212
- int_part = (int_part / power).to_i
1213
- end
1214
- end
1215
- id
1216
- end
1217
-
1218
- #
1219
- # before adding or subtracting two LongDecimal numbers
1220
- # it is mandatory to set them to the same scale. The maximum of the
1221
- # two summands is used, in order to avoid loosing any information.
1222
- # this method is mostly for internal use
1223
- #
1224
- def equalize_scale(other)
1225
- o, s = coerce(other)
1226
- if (s.kind_of? LongDecimal) then
1227
- # make sure Floats do not mess up our number of significant digits when adding
1228
- if (other.kind_of? Float) then
1229
- o = o.round_to_scale(s.scale, ROUND_HALF_UP)
1230
- else
1231
- new_scale = [s.scale, o.scale].max
1232
- s = s.round_to_scale(new_scale)
1233
- o = o.round_to_scale(new_scale)
1234
- end
1235
- end
1236
- return s, o
1237
- end
1238
-
1239
- #
1240
- # before dividing two LongDecimal numbers, it is mandatory to set
1241
- # make them both to integers, so the result is simply expressable as
1242
- # a rational
1243
- # this method is mostly for internal use
1244
- #
1245
- def anti_equalize_scale(other)
1246
- o, s = coerce(other)
1247
- if (s.kind_of? LongDecimal) then
1248
- exponent = [s.scale, o.scale].max
1249
- factor = 10**exponent
1250
- s *= factor
1251
- o *= factor
1252
- s = s.round_to_scale(0)
1253
- o = o.round_to_scale(0)
1254
- end
1255
- return s, o
1256
- end
1257
-
1258
- #
1259
- # successor as needed for using LongDecimal in ranges
1260
- # it needs to be observed that this is usually not an increment by
1261
- # 1, but by 1/10**scale.
1262
- #
1263
- def succ
1264
- LongDecimal(int_val + 1, scale)
1265
- end
1266
-
1267
- alias next succ
1268
-
1269
- #
1270
- # predecessor (opposite of successor)
1271
- # it needs to be observed that this is usually not an decrement by
1272
- # 1, but by 1/10**scale.
1273
- #
1274
- def pred
1275
- LongDecimal(int_val - 1, scale)
1276
- end
1277
-
1278
- #
1279
- # self + 1
1280
- #
1281
- def inc
1282
- self + 1
1283
- end
1284
-
1285
- #
1286
- # self - 1
1287
- #
1288
- def dec
1289
- self - 1
1290
- end
1291
-
1292
- #
1293
- # self += 1
1294
- #
1295
- def inc!
1296
- @int_val += denominator
1297
- end
1298
-
1299
- #
1300
- # self -= 1
1301
- #
1302
- def dec!
1303
- @int_val -= denominator
1304
- end
1305
-
1306
- #
1307
- # return the unit by which self is incremented by succ
1308
- #
1309
- def unit
1310
- LongDecimal(1, scale)
1311
- end
1312
-
1313
- #
1314
- # apply unary +
1315
- # (returns self)
1316
- #
1317
- def +@
1318
- self
1319
- end
1320
-
1321
- #
1322
- # apply unary -
1323
- # (returns negated self)
1324
- #
1325
- def -@
1326
- if self.zero? then
1327
- self
1328
- else
1329
- LongDecimal(-int_val, scale)
1330
- end
1331
- end
1332
-
1333
- #
1334
- # add two numbers
1335
- # if both can immediately be expressed as LongDecimal, the result is
1336
- # a LongDecimal as well. The number of digits after the decimal
1337
- # point is the max of the scales of the summands
1338
- # if LongDecimal does not cover the two summands, call addition of
1339
- # Complex, Float or LongRationalQuot
1340
- #
1341
- def +(other)
1342
- s, o = equalize_scale(other)
1343
- if s.kind_of? LongDecimal then
1344
- LongDecimal(s.int_val + o.int_val, s.scale)
1345
- else
1346
- s + o
1347
- end
1348
- end
1349
-
1350
- #
1351
- # subtract two numbers
1352
- # if both can immediately be expressed as LongDecimal, the result is
1353
- # a LongDecimal as well. The number of digits after the decimal
1354
- # point is the max of the scales of self and other.
1355
- # if LongDecimal does not cover self and other, the subtraction of
1356
- # Complex, Float or LongRationalQuot is used
1357
- #
1358
- def -(other)
1359
- s, o = equalize_scale(other)
1360
- if s.kind_of? LongDecimal then
1361
- LongDecimal(s.int_val - o.int_val, s.scale)
1362
- else
1363
- s - o
1364
- end
1365
- end
1366
-
1367
- #
1368
- # multiply two numbers
1369
- # if both can immediately be expressed as LongDecimal, the result is
1370
- # a LongDecimal as well. The number of digits after the decimal
1371
- # point is the sum of the scales of both factors.
1372
- # if LongDecimal does not cover self and other, the multiplication of
1373
- # Complex, Float or LongRationalQuot is used
1374
- #
1375
- def *(other)
1376
- o, s = coerce(other)
1377
- if s.kind_of? LongDecimal then
1378
- LongDecimal(s.int_val * o.int_val, s.scale + o.scale)
1379
- else
1380
- s * o
1381
- end
1382
- end
1383
-
1384
- #
1385
- # divide self by other and round result to scale of self using the
1386
- # given rounding mode
1387
- #
1388
- def divide(other, rounding_mode)
1389
- divide_s(other, nil, rounding_mode)
1390
- end
1391
-
1392
- #
1393
- # divide self by other and round result to new_scale using the
1394
- # given rounding mode. If new_scale is nil, use scale of self.
1395
- #
1396
- def divide_s(other, new_scale, rounding_mode)
1397
- q = self / other
1398
- if (q.kind_of? Float) then
1399
- q = LongDecimal(q)
1400
- end
1401
- if (q.kind_of? LongDecimal) || (q.kind_of? LongDecimalQuot) then
1402
- if (new_scale.nil?) then
1403
- new_scale = q.scale
1404
- end
1405
- q.round_to_scale(new_scale, rounding_mode)
1406
- else
1407
- q
1408
- end
1409
- end
1410
-
1411
- #
1412
- # divide self by other and return result as Rational, if other
1413
- # allowed exact calculations.
1414
- #
1415
- def rdiv(other)
1416
- q = self / other
1417
- if (q.kind_of? LongDecimalQuot) then
1418
- q.to_r
1419
- else
1420
- q
1421
- end
1422
- end
1423
-
1424
- #
1425
- # divide self by other and return result as LongDecimalQuot
1426
- # because division does not have an obvious rounding rule like
1427
- # addition, subtraction and multiplication, the result needs to be
1428
- # rounded afterwards to become a LongDecimal again. This way
1429
- # calculations can still be done in the natural readable way using +,
1430
- # -, *, and /, but the rounding can be provided later.
1431
- # It is very important in complicated calculations put the rounding
1432
- # steps in the right places, usually after having performed a division.
1433
- #
1434
- def /(other)
1435
- o, s = coerce(other)
1436
- if (s.kind_of? LongDecimal) then
1437
- LongDecimalQuot(s, o)
1438
- else
1439
- s / o
1440
- end
1441
- end
1442
-
1443
- #
1444
- # power of self (LongDecimal) with other.
1445
- # if other is expressable as non-negative integer, the power is what
1446
- # would be obtained by successive multiplications.
1447
- # if other is expressable as negative integer, the power is a
1448
- # LongDecimalQuot as would result by successive division, but with
1449
- # the same scale as the positive power would get. Explicit rounding
1450
- # is needed to convert into a LongDecimal again
1451
- # in all other cases, self is converted into a Rational prior to
1452
- # applying power, usually resulting in a Float as power.
1453
- #
1454
- def **(other)
1455
- if ((other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)) && other.is_int? then
1456
- other = other.to_i
1457
- end
1458
- if other.kind_of? Integer then
1459
- if other >= 0 then
1460
- LongDecimal(int_val ** other, scale * other)
1461
- else
1462
- abs_other = -other
1463
- new_scale = abs_other * scale
1464
- LongDecimalQuot(Rational(10 ** new_scale, int_val ** abs_other), new_scale)
1465
- end
1466
- else
1467
- if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
1468
- other = other.to_r
1469
- end
1470
- self.to_r ** other
1471
- end
1472
- end
1473
-
1474
- #
1475
- # do integer division with remainder, returning two values
1476
- #
1477
- def divmod(other)
1478
- if (other.kind_of? Complex) then
1479
- raise TypeError, "divmod not supported for Complex"
1480
- end
1481
- q = (self / other).to_i
1482
- return q, self - other * q
1483
- end
1484
-
1485
- #
1486
- # remainder of integer division by other
1487
- #
1488
- def %(other)
1489
- q, r = divmod other
1490
- r
1491
- end
1492
-
1493
- #
1494
- # performs bitwise AND between self and other
1495
- #
1496
- def &(other)
1497
- s, o = equalize_scale(other)
1498
- if s.kind_of? LongDecimal then
1499
- LongDecimal(s.int_val & o.int_val, s.scale)
1500
- else
1501
- s & o
1502
- end
1503
- end
1504
-
1505
- #
1506
- # performs bitwise OR between self and other
1507
- #
1508
- def |(other)
1509
- s, o = equalize_scale(other)
1510
- if s.kind_of? LongDecimal then
1511
- LongDecimal(s.int_val | o.int_val, s.scale)
1512
- else
1513
- s | o
1514
- end
1515
- end
1516
-
1517
- #
1518
- # performs bitwise XOR between self and other
1519
- #
1520
- def ^(other)
1521
- s, o = equalize_scale(other)
1522
- if s.kind_of? LongDecimal then
1523
- LongDecimal(s.int_val ^ o.int_val, s.scale)
1524
- else
1525
- s ^ o
1526
- end
1527
- end
1528
-
1529
- #
1530
- # bitwise inversion
1531
- #
1532
- def ~
1533
- LongDecimal(~int_val, scale)
1534
- end
1535
-
1536
- #
1537
- # performs bitwise left shift of self by other
1538
- #
1539
- def <<(other)
1540
- unless (other.kind_of? Integer) && other >= 0 then
1541
- raise TypeError, "cannot shift by something other than integer >= 0"
1542
- end
1543
- LongDecimal(s.int_val << other, s.scale)
1544
- end
1545
-
1546
- #
1547
- # performs bitwise right shift of self by other
1548
- #
1549
- def >>(other)
1550
- unless (other.kind_of? Integer) && other >= 0 then
1551
- raise TypeError, "cannot shift by something other than integer >= 0"
1552
- end
1553
- LongDecimal(s.int_val >> other, s.scale)
1554
- end
1555
-
1556
- #
1557
- # gets binary digit of self
1558
- #
1559
- def [](other)
1560
- int_val[other]
1561
- end
1562
-
1563
- #
1564
- # gets size of int_val
1565
- #
1566
- def size
1567
- int_val.size
1568
- end
1569
-
1570
- #
1571
- # divide by 10**n
1572
- #
1573
- def move_point_left(n)
1574
- raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
1575
- if (n >= 0) then
1576
- move_point_left_int(n)
1577
- else
1578
- move_point_right_int(-n)
1579
- end
1580
- end
1581
-
1582
- #
1583
- # multiply by 10**n
1584
- #
1585
- def move_point_right(n)
1586
- raise TypeError, "only implemented for Fixnum" unless n.kind_of? Fixnum
1587
- if (n < 0) then
1588
- move_point_left_int(-n)
1589
- else
1590
- move_point_right_int(n)
1591
- end
1592
- end
1593
-
1594
- #
1595
- # internal method
1596
- # divide by 10**n
1597
- #
1598
- def move_point_left_int(n)
1599
- raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
1600
- LongDecimal(int_val, scale + n)
1601
- end
1602
-
1603
- #
1604
- # internal method
1605
- # multiply by 10**n
1606
- #
1607
- def move_point_right_int(n)
1608
- raise TypeError, "only implemented for Fixnum >= 0" unless n >= 0
1609
- if (n > scale) then
1610
- LongDecimal(int_val * 10**(n-scale), 0)
1611
- else
1612
- LongDecimal(int_val, scale-n)
1613
- end
1614
- end
1615
-
1616
- protected :move_point_left_int, :move_point_right_int
1617
-
1618
- #
1619
- # calculate the square of self
1620
- #
1621
- def square
1622
- self * self
1623
- end
1624
-
1625
- def sqrt(new_scale, rounding_mode)
1626
- raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
1627
- raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
1628
- raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless rounding_mode.kind_of? RoundingModeClass
1629
-
1630
- new_scale1 = new_scale + 1
1631
- old_scale = (new_scale1 << 1)
1632
- x = round_to_scale(old_scale, rounding_mode)
1633
- root, rem = LongMath.sqrtb_with_remainder(x.int_val)
1634
- if (rem > 0 && (rounding_mode == ROUND_HALF_EVEN || rounding_mode == ROUND_HALF_DOWN)) then
1635
- rounding_mode = ROUND_HALF_UP
1636
- end
1637
- y = LongDecimal(root, new_scale1)
1638
- y.round_to_scale(new_scale, rounding_mode)
1639
- end
1640
-
1641
-
1642
- #
1643
- # calculate the multiplicative inverse
1644
- #
1645
- def reciprocal
1646
- 1 / self
1647
- end
1648
-
1649
- alias inverse reciprocal
1650
-
1651
- #
1652
- # Absolute value
1653
- #
1654
- def abs
1655
- LongDecimal(int_val.abs, scale)
1656
- end
1657
-
1658
- #
1659
- # square of absolute value
1660
- # happens to be the square
1661
- #
1662
- alias abs2 square
1663
-
1664
- #
1665
- # Compares the two numbers.
1666
- # returns -1 if self < other
1667
- # 0 if self-other = 0
1668
- # +1 if self > other
1669
- # it needs to be observed, that
1670
- # x == y implies (x <=> y) == 0
1671
- # but not
1672
- # (x <=> y) == 0 implies x == y
1673
- # because == also takes the scale into account and considers two
1674
- # numbers only equal, if they have the same number of potentially
1675
- # zero digits after the decimal point.
1676
- #
1677
- def <=> (other)
1678
- diff = (self - other)
1679
- if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
1680
- diff.sgn
1681
- else
1682
- diff <=> 0
1683
- end
1684
- end
1685
-
1686
- #
1687
- # <=>-comparison for the scales
1688
- #
1689
- def scale_ufo(other)
1690
- raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
1691
- self.scale <=> other.scale
1692
- end
1693
-
1694
- #
1695
- # ==-comparison for the scales
1696
- #
1697
- def scale_equal(other)
1698
- scale_ufo(other).zero?
1699
- end
1700
-
1701
- #
1702
- # return a pair o, s resembling other, self, but potentially
1703
- # converted to compatible types and ready for
1704
- # arithmetic operations.
1705
- #
1706
- def coerce(other)
1707
- if other.kind_of? LongDecimal then
1708
- return other, self
1709
- elsif other.kind_of? LongDecimalQuot then
1710
- return other, LongDecimalQuot(self.to_r, scale)
1711
- elsif other.kind_of? Rational then
1712
- sc = scale
1713
- o = LongDecimalQuot(other, sc)
1714
- s = LongDecimalQuot(self.to_r, sc)
1715
- return o, s
1716
- elsif (other.kind_of? Integer) || (other.kind_of? Float) then
1717
- other = LongDecimal(other)
1718
- if (other.scale > scale) then
1719
- other = other.round_to_scale(scale, ROUND_HALF_UP)
1720
- end
1721
- return other, self
1722
- elsif other.kind_of? BigDecimal then
1723
- s, o = other.coerce(self.to_bd)
1724
- return o, s
1725
- elsif other.kind_of? Complex then
1726
- # s, o = other.coerce(Complex(self.to_bd, 0))
1727
- s, o = other.coerce(Complex(self.to_f, 0))
1728
- return o, s
1729
- elsif (other.kind_of? Float) && size > 8 then
1730
- return coerce(BigDecimal(other.to_s))
1731
- elsif other.kind_of? Numeric then
1732
- s, o = other.coerce(self.to_f)
1733
- return o, s
1734
- else
1735
- raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimal"
1736
- end
1737
- end
1738
-
1739
- #
1740
- # is self expressable as an integer without loss of digits?
1741
- #
1742
- def is_int?
1743
- scale == 0 || int_val % 10**scale == 0
1744
- end
1745
-
1746
- #
1747
- # get the sign of self
1748
- # -1 if self < 0
1749
- # 0 if self is 0 (with any number of 0s after the decimal point)
1750
- # +1 if self > 0
1751
- #
1752
- def sgn
1753
- int_val <=> 0
1754
- end
1755
-
1756
- alias signum sgn
1757
- alias sign sgn
1758
-
1759
- #
1760
- # comparison of self with other for equality
1761
- # takes into account the values expressed by self and other and the
1762
- # equality of the number of digits.
1763
- #
1764
- def ==(other)
1765
- # (other.kind_of? LongDecimal) && (self <=> other) == 0 && self.scale == other.scale
1766
- (other.kind_of? LongDecimal) && self.int_val == other.int_val && self.scale == other.scale
1767
- end
1768
-
1769
- #
1770
- # check if the number expressed by self is 0 (zero)
1771
- # with any number of 0s after the decimal point.
1772
- #
1773
- def zero?
1774
- int_val.zero?
1775
- end
1776
-
1777
- #
1778
- # check if the number expressed by self is 1 (one)
1779
- # with any number of 0s after the decimal point.
1780
- #
1781
- def one?
1782
- (self-1).zero?
1783
- end
1784
-
1785
- #
1786
- # Returns a hash code for the complex number.
1787
- #
1788
- def hash
1789
- int_val.hash ^ scale.hash
1790
- end
1791
-
1792
- #
1793
- # Returns "<tt>LongDecimal(<i>int_val</i>, <i>scale</i>)</tt>".
1794
- #
1795
- def inspect
1796
- sprintf("LongDecimal(%s, %s)", int_val.inspect, scale.inspect)
1797
- end
1798
-
1799
- end # LongDecimal
1800
-
1801
- #
1802
- # This class is used for storing intermediate results after having
1803
- # performed a division. The division cannot be completed without
1804
- # providing additional information on how to round the result.
1805
- #
1806
- class LongDecimalQuot < Numeric
1807
-
1808
- @RCS_ID='-$Id: longdecimal.rb,v 1.9 2006/03/18 06:03:58 bk1 Exp $-'
1809
-
1810
- include LongDecimalRoundingMode
1811
-
1812
- #
1813
- # constructor
1814
- # first, second is either a pair of LongDecimals or a Rational and an Integer
1815
- # The resulting LongDecimal will contain a rational obtained by
1816
- # dividing the two LongDecimals or by taking the Rational as it is.
1817
- # The scale is there to provide a default rounding precision for
1818
- # conversion to LongDecimal, but it has no influence on the value
1819
- # expressed by the LongDecimalQuot
1820
- #
1821
- def LongDecimalQuot.new!(first, second)
1822
- new(first, second)
1823
- end
1824
-
1825
- #
1826
- # create a new LongDecimalQuot from a rational and a scale or a
1827
- # pair of LongDecimals
1828
- #
1829
- def initialize(first, second)
1830
- if ((first.kind_of? Rational) || (first.kind_of? Integer)) && (second.kind_of? Integer) then
1831
- @rat = Rational(first.numerator, first.denominator)
1832
- @scale = second
1833
- elsif (first.kind_of? LongDecimal) && (second.kind_of? LongDecimal) then
1834
- orig_scale = first.scale
1835
- first, second = first.anti_equalize_scale(second)
1836
- @rat = Rational(first.to_i, second.to_i)
1837
- @scale = orig_scale
1838
- else
1839
- raise TypeError, "parameters must be (LongDecimal, LongDecimal) or (Rational, Integer): first=#{first.inspect} second=#{second.inspect}";
1840
- end
1841
- end
1842
-
1843
- attr_reader :scale, :rat
1844
-
1845
- # def scale
1846
- # @scale
1847
- # end
1848
-
1849
- # def rat
1850
- # @rat
1851
- # end
1852
-
1853
- #
1854
- # numerator of the included rational number.
1855
- # LongDecimals should duck type like Rationals
1856
- #
1857
- def numerator
1858
- rat.numerator
1859
- end
1860
-
1861
- #
1862
- # denominator of the included rational number.
1863
- # LongDecimals should duck type like Rationals
1864
- #
1865
- def denominator
1866
- rat.denominator
1867
- end
1868
-
1869
- #
1870
- # alter scale (only for internal use)
1871
- #
1872
- def scale=(s)
1873
- raise TypeError, "non integer arg \"#{s.inspect}\"" if ! s.kind_of? Integer
1874
- raise TypeError, "negative arg \"#{s.inspect}\"" if s < 0
1875
- @scale = s
1876
- end
1877
-
1878
- private :scale=
1879
-
1880
- #
1881
- # conversion to string. Based on the conversion of Rational
1882
- #
1883
- def to_s
1884
- str = @rat.to_s
1885
- str + "[" + scale.to_s + "]"
1886
- end
1887
-
1888
- #
1889
- # conversion to rational
1890
- #
1891
- def to_r
1892
- Rational(numerator, denominator)
1893
- end
1894
-
1895
- #
1896
- # convert into Float
1897
- #
1898
- def to_f
1899
- to_r.to_f
1900
- end
1901
-
1902
- #
1903
- # convert into Integer
1904
- #
1905
- def to_i
1906
- to_r.to_i
1907
- end
1908
-
1909
- #
1910
- # conversion to LongDecimal using the internal scale
1911
- #
1912
- def to_ld
1913
- round_to_scale(scale, ROUND_HALF_UP)
1914
- end
1915
-
1916
- #
1917
- # unary plus returns self
1918
- #
1919
- def +@
1920
- self
1921
- end
1922
-
1923
- #
1924
- # unary minus returns negation of self
1925
- # leaves self unchanged.
1926
- #
1927
- def -@
1928
- if self.zero? then
1929
- self
1930
- else
1931
- LongDecimalQuot(-rat, scale)
1932
- end
1933
- end
1934
-
1935
- #
1936
- # addition
1937
- # if other can be converted into LongDecimalQuot, add as
1938
- # LongDecimalQuot, using the addition of Rationals
1939
- # otherwise use BigDecimal, Complex or Float
1940
- #
1941
- def +(other)
1942
- o, s = coerce(other)
1943
- if (s.kind_of? LongDecimalQuot) then
1944
- LongDecimalQuot(s.rat + o.rat, [s.scale, o.scale].max)
1945
- else
1946
- s + o
1947
- end
1948
- end
1949
-
1950
- def -(other)
1951
- o, s = coerce(other)
1952
- if (s.kind_of? LongDecimalQuot) then
1953
- LongDecimalQuot(s.rat - o.rat, [s.scale, o.scale].max)
1954
- else
1955
- s - o
1956
- end
1957
- end
1958
-
1959
- def *(other)
1960
- o, s = coerce(other)
1961
- if (s.kind_of? LongDecimalQuot) then
1962
- LongDecimalQuot(s.rat * o.rat, s.scale + o.scale)
1963
- else
1964
- s * o
1965
- end
1966
- end
1967
-
1968
- def /(other)
1969
- o, s = coerce(other)
1970
- if (s.kind_of? LongDecimalQuot) then
1971
- LongDecimalQuot(s.rat / o.rat, scale)
1972
- else
1973
- s / o
1974
- end
1975
- end
1976
-
1977
- def **(other)
1978
- if (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot) then
1979
- if other.is_int? then
1980
- other = other.to_i
1981
- else
1982
- other = other.to_r
1983
- end
1984
- end
1985
- rat_result = rat ** other
1986
- if (rat_result.kind_of? Rational) then
1987
- if (other.kind_of? Integer) && other >= 0 then
1988
- new_scale = scale * other
1989
- else
1990
- new_scale = scale
1991
- end
1992
- LongDecimalQuot(rat_result, new_scale)
1993
- else
1994
- rat_result
1995
- end
1996
- end
1997
-
1998
- def divmod(other)
1999
- if (other.kind_of? Complex) then
2000
- raise TypeError, "divmod not supported for Complex"
2001
- end
2002
- q = (self / other).to_i
2003
- return q, self - other * q
2004
- end
2005
-
2006
- def %(other)
2007
- q, r = divmod other
2008
- r
2009
- end
2010
-
2011
- # def %(other)
2012
- # o, s = coerce(other)
2013
- # if (s.kind_of? LongDecimalQuot) then
2014
- # LongDecimalQuot(s.rat % o.rat, scale)
2015
- # else
2016
- # s % o
2017
- # end
2018
- # end
2019
-
2020
- def square
2021
- self * self
2022
- end
2023
-
2024
- #
2025
- # calculate the multiplicative inverse
2026
- #
2027
- def reciprocal
2028
- 1 / self
2029
- end
2030
-
2031
- #
2032
- # Absolute value
2033
- #
2034
- def abs
2035
- LongDecimalQuot(rat.abs, scale)
2036
- end
2037
-
2038
- def abs2
2039
- self.abs.square
2040
- end
2041
-
2042
- #
2043
- # convert LongDecimalQuot to LongDecimal with the given precision
2044
- # and the given rounding mode
2045
- #
2046
- def round_to_scale(new_scale = @scale, mode = ROUND_UNNECESSARY)
2047
-
2048
- raise TypeError, "new_scale #{new_scale.inspect} must be integer" unless new_scale.kind_of? Integer
2049
- raise TypeError, "new_scale #{new_scale.inspect} must be >= 0" unless new_scale >= 0
2050
- raise TypeError, "mode #{mode.inspect} must be legal rounding mode" unless mode.kind_of? RoundingModeClass
2051
-
2052
- factor = 10**new_scale
2053
- sign_quot = numerator <=> 0
2054
- if sign_quot == 0 then
2055
- return LongDecimal(0, new_scale)
2056
- end
2057
- prod = numerator * factor
2058
- divisor = denominator
2059
- quot, rem = prod.divmod(divisor)
2060
- sign_rem = rem <=> 0
2061
- if (sign_rem == 0)
2062
- return LongDecimal(quot, new_scale)
2063
- end
2064
- raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem <= 0
2065
- if (sign_quot < 0) then
2066
- rem -= divisor
2067
- quot += 1
2068
- sign_rem = rem <=> 0
2069
- raise Error, "signs do not match self=#{self.to_s} f=#{factor} prod=#{prod} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem >= 0
2070
- end
2071
-
2072
- if mode == ROUND_UNNECESSARY then
2073
- raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, remainder #{rem.to_s} is not zero"
2074
- end
2075
-
2076
- if (mode == ROUND_CEILING)
2077
- mode = (sign_quot > 0) ? ROUND_UP : ROUND_DOWN
2078
- elsif (mode == ROUND_FLOOR)
2079
- mode = (sign_quot < 0) ? ROUND_UP : ROUND_DOWN
2080
- else
2081
- abs_rem = rem.abs
2082
- half = (abs_rem << 1) <=> denominator
2083
- if (mode == ROUND_HALF_UP || mode == ROUND_HALF_DOWN || mode == ROUND_HALF_EVEN) then
2084
- if (half < 0) then
2085
- mode = ROUND_DOWN
2086
- elsif half > 0 then
2087
- mode = ROUND_UP
2088
- else
2089
- # half == 0
2090
- if (mode == ROUND_HALF_UP) then
2091
- mode = ROUND_UP
2092
- elsif (mode == ROUND_HALF_DOWN) then
2093
- mode = ROUND_DOWN
2094
- else
2095
- # mode == ROUND_HALF_EVEN
2096
- mode = (quot[0] == 1 ? ROUND_UP : ROUND_DOWN)
2097
- end
2098
- end
2099
- end
2100
- end
2101
-
2102
- if mode == ROUND_UP
2103
- quot += sign_quot
2104
- end
2105
- new_int_val = quot
2106
- LongDecimal(new_int_val, new_scale)
2107
- end
2108
-
2109
- #
2110
- # prepare binary operation of other with LongDecimalQuot
2111
- # Integer, LongDecimal, Rational and LongDecimalQuot can be
2112
- # expressed as LongDecimalQuot, using the scale of self in case of
2113
- # Integer and Rational. Floats can be approximated by LongDecimals
2114
- # and thus be expressed as LongDecimalQuot
2115
- # In case of BigDecimal, Complex or any unknown type, convert self
2116
- # to BigDecimal or Float.
2117
- #
2118
- def coerce(other)
2119
- if other.kind_of? LongDecimal then
2120
- return LongDecimalQuot(other.to_r, other.scale), self
2121
- elsif other.kind_of? LongDecimalQuot then
2122
- return other, self
2123
- elsif other.kind_of? Rational then
2124
- s = scale
2125
- return LongDecimalQuot(other, s), self
2126
- elsif (other.kind_of? Integer) then
2127
- return LongDecimalQuot(other.to_r, scale), self
2128
- elsif other.kind_of? Float then
2129
- return LongDecimalQuot(other.to_ld.to_r, scale), self
2130
- elsif other.kind_of? BigDecimal then
2131
- s, o = other.coerce(self.to_bd)
2132
- elsif other.kind_of? Numeric then
2133
- s, o = other.coerce(self.to_f)
2134
- return o, s
2135
- else
2136
- raise TypeError, "unsupported type #{other.inspect} for coerce of LongDecimalQuot"
2137
- end
2138
- end
2139
-
2140
- #
2141
- # compare two numbers for equality.
2142
- # The LongDecimalQuot self is considered == to other if and only if
2143
- # other is also LongDecimalQuot, expresses the same value and has the
2144
- # same scale.
2145
- # It needs to be observed that scale does not influence the value expressed
2146
- # by the number, but only how rouding is performed by default if no
2147
- # explicit number of digits after the decimal point is given. But
2148
- # scale needs to match for equality.
2149
- #
2150
- def ==(other)
2151
- (other.kind_of? LongDecimalQuot) && (self <=> other) == 0 && self.scale == other.scale
2152
- end
2153
-
2154
- #
2155
- # Compares the two numbers for < and > etc.
2156
- #
2157
- def <=> (other)
2158
- diff = (self - other)
2159
- if (diff.kind_of? LongDecimal) || (diff.kind_of? LongDecimalQuot) then
2160
- diff.sgn
2161
- else
2162
- diff <=> 0
2163
- end
2164
- end
2165
-
2166
- #
2167
- # compare scales with <=>
2168
- #
2169
- def scale_ufo(other)
2170
- raise TypeError, "only works for LongDecimal and LongDecimalQuot" unless (other.kind_of? LongDecimal) || (other.kind_of? LongDecimalQuot)
2171
- self.scale <=> other.scale
2172
- end
2173
-
2174
- #
2175
- # check if scales are equal
2176
- #
2177
- def scale_equal(other)
2178
- scale_ufo(other).zero?
2179
- end
2180
-
2181
- #
2182
- # is self expressable as an integer without loss of digits?
2183
- #
2184
- def is_int?
2185
- denominator == 1
2186
- end
2187
-
2188
- #
2189
- # sign of self
2190
- #
2191
- def sgn
2192
- numerator <=> 0
2193
- end
2194
- alias signum sgn
2195
- alias sign sgn
2196
-
2197
- #
2198
- # Returns a hash code for the complex number.
2199
- #
2200
- def hash
2201
- rat.hash ^ scale.hash
2202
- end
2203
-
2204
-
2205
- #
2206
- # Returns "<tt>LongDecimalQuot(<i>int_val</i>, <i>scale</i>, <i>num</i>, <i>denom</i>)</tt>".
2207
- #
2208
- def inspect
2209
- sprintf("LongDecimalQuot(Rational(%s, %s), %s)", numerator.inspect, denominator.inspect, scale.inspect)
2210
- end
2211
-
2212
- end # LongDecimalQuot
2213
-
2214
- #
2215
- # Creates a LongDecimal number. +a+ and +b+ should be Numeric.
2216
- #
2217
- def LongDecimal(a, b = 0)
2218
- if b == 0 && (a.kind_of? LongDecimal) then
2219
- a
2220
- else
2221
- LongDecimal.new!(a, b)
2222
- end
2223
- end
2224
-
2225
- #
2226
- # construct a LongDecimalQuot from the given parameters
2227
- # 1st case: both are LongDecimals
2228
- # 2nd case: first is Rational, second is scale
2229
- #
2230
- def LongDecimalQuot(first, second)
2231
- LongDecimalQuot.new!(first, second)
2232
- end
2233
-
2234
-
2235
- class Numeric
2236
-
2237
- #
2238
- # convert self to LongDecimal
2239
- #
2240
- def to_ld
2241
- LongDecimal(self)
2242
- end
2243
-
2244
- end # Numeric
2245
-
2246
- # end of file longdecimal.rb