long-decimal 1.00.01 → 1.00.02
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +15 -0
- data/README +41 -41
- data/Rakefile +19 -73
- data/lib/long-decimal-extra.rb +5 -672
- data/lib/long-decimal.rb +1609 -141
- data/lib/long-decimal/version.rb +3 -0
- data/long-decimal.gemspec +22 -0
- data/test/testlongdecimal.rb +2498 -651
- data/test/testlongdeclib.rb +20 -10
- data/test/testrandlib.rb +6 -5
- data/test/testrandom.rb +30 -14
- data/test/testrandpower.rb +22 -7
- data/tex/long-decimal.pdf +0 -0
- data/tex/long-decimal.tex +420 -0
- metadata +72 -64
- data/VERSION +0 -1
- data/install.rb +0 -18
- data/make_doc.rb +0 -12
- data/test/testlongdecimal-extra.rb +0 -1750
- data/test/testlongdecimal-performance.rb +0 -357
- data/test/testrandom-extra.rb +0 -80
- data/version.rb +0 -39
data/lib/long-decimal.rb
CHANGED
@@ -7,8 +7,8 @@
|
|
7
7
|
# additional functionality, mostly transcendental functions,
|
8
8
|
# may be found in long-decimal-extra.rb
|
9
9
|
#
|
10
|
-
# CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/long-decimal.rb,v 1.
|
11
|
-
# CVS-Label: $Name:
|
10
|
+
# CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/long-decimal.rb,v 1.87 2011/01/30 20:01:40 bk1 Exp $
|
11
|
+
# CVS-Label: $Name: $
|
12
12
|
# Author: $Author: bk1 $ (Karl Brodowsky)
|
13
13
|
#
|
14
14
|
|
@@ -26,7 +26,106 @@ BYTE_SIZE_OF_ONE = 1.size
|
|
26
26
|
#
|
27
27
|
module LongDecimalRoundingMode
|
28
28
|
|
29
|
-
|
29
|
+
private
|
30
|
+
|
31
|
+
RoundingMinorMode = Struct.new(:name, :part)
|
32
|
+
|
33
|
+
# exactly on the boundary round away from zero
|
34
|
+
MINOR_UP = RoundingMinorMode.new(:MINOR_UP, :_UP)
|
35
|
+
# exactly on the boundary round towards zero
|
36
|
+
MINOR_DOWN = RoundingMinorMode.new(:MINOR_DOWN, :_DOWN)
|
37
|
+
# exactly on the boundary round towards positive infinitiy (to the higher of the two possible values)
|
38
|
+
MINOR_CEILING = RoundingMinorMode.new(:MINOR_CEILING, :_CEILING)
|
39
|
+
# exactly on the boundary round towards negative infinitiy (to the lower of the two possible values)
|
40
|
+
MINOR_FLOOR = RoundingMinorMode.new(:MINOR_FLOOR, :_FLOOR)
|
41
|
+
# exactly on the boundary pick the one with even end digit
|
42
|
+
MINOR_EVEN = RoundingMinorMode.new(:MINOR_EVEN, :_EVEN)
|
43
|
+
# exactly on the boundary pick the one with even end digit
|
44
|
+
MINOR_ODD = RoundingMinorMode.new(:MINOR_ODD, :_ODD)
|
45
|
+
# (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)
|
46
|
+
# MINOR_05UP = RoundingMinorMode.new(:MINOR_05UP, :_05UP)
|
47
|
+
# for major modes that are completely defined by themselves and do not need to rely on minor modes for the boundary stuff
|
48
|
+
MINOR_UNUSED = RoundingMinorMode.new(:MINOR_UNUSED, "")
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
ALL_MINOR = [ MINOR_UP, MINOR_DOWN, MINOR_CEILING, MINOR_FLOOR, MINOR_EVEN, MINOR_ODD ]
|
53
|
+
|
54
|
+
NO_MINOR = [ MINOR_UNUSED ]
|
55
|
+
|
56
|
+
# which mode is to be used instead when we do an multiplicative inversion?
|
57
|
+
MUL_INVERSE_MINOR_MODE = {
|
58
|
+
MINOR_UNUSED => MINOR_UNUSED,
|
59
|
+
MINOR_UP => MINOR_DOWN,
|
60
|
+
MINOR_DOWN => MINOR_UP,
|
61
|
+
MINOR_CEILING => MINOR_FLOOR,
|
62
|
+
MINOR_FLOOR => MINOR_CEILING,
|
63
|
+
MINOR_EVEN => MINOR_EVEN,
|
64
|
+
MINOR_ODD => MINOR_ODD,
|
65
|
+
}
|
66
|
+
|
67
|
+
# which mode is to be used instead when we do an additive inversion?
|
68
|
+
ADD_INVERSE_MINOR_MODE = {
|
69
|
+
MINOR_UNUSED => MINOR_UNUSED,
|
70
|
+
MINOR_UP => MINOR_UP,
|
71
|
+
MINOR_DOWN => MINOR_DOWN,
|
72
|
+
MINOR_CEILING => MINOR_FLOOR,
|
73
|
+
MINOR_FLOOR => MINOR_CEILING,
|
74
|
+
MINOR_EVEN => MINOR_EVEN,
|
75
|
+
MINOR_ODD => MINOR_ODD,
|
76
|
+
}
|
77
|
+
|
78
|
+
RoundingMajorMode = Struct.new(:name, :part, :minor)
|
79
|
+
|
80
|
+
# round away from zero
|
81
|
+
MAJOR_UP = RoundingMajorMode.new(:MAJOR_UP, :UP, NO_MINOR)
|
82
|
+
# round towards zero
|
83
|
+
MAJOR_DOWN = RoundingMajorMode.new(:MAJOR_DOWN, :DOWN, NO_MINOR)
|
84
|
+
# pick the higher of the two possible values
|
85
|
+
MAJOR_CEILING = RoundingMajorMode.new(:MAJOR_CEILING, :CEILING, NO_MINOR)
|
86
|
+
# pick the lower of the two possible values
|
87
|
+
MAJOR_FLOOR = RoundingMajorMode.new(:MAJOR_FLOOR, :FLOOR, NO_MINOR)
|
88
|
+
# use the original value, if it is not already rounded, raise an error
|
89
|
+
MAJOR_UNNECESSARY = RoundingMajorMode.new(:MAJOR_UNNECESSARY, :UNNECESSARY, NO_MINOR)
|
90
|
+
# (away from zero if last digit after rounding towards zero would have been 0 or 5; otherwise towards zero)
|
91
|
+
# MAJOR_05UP = RoundingMinorMode.new(:MAJOR_05UP, :_05UP, NO_MINOR)
|
92
|
+
|
93
|
+
# the arithmetic mean of two adjacent rounded values is the boundary
|
94
|
+
MAJOR_HALF = RoundingMajorMode.new(:MAJOR_HALF, :HALF, ALL_MINOR)
|
95
|
+
# the arithmetic mean of two adjacent rounded values is the boundary
|
96
|
+
MAJOR_GEOMETRIC = RoundingMajorMode.new(:MAJOR_GEOMETRIC, :GEOMETRIC, ALL_MINOR)
|
97
|
+
# the harmonic mean of two adjacent rounded values is the boundary
|
98
|
+
MAJOR_HARMONIC = RoundingMajorMode.new(:MAJOR_HARMONIC, :HARMONIC, ALL_MINOR)
|
99
|
+
# the quadratic mean of two adjacent rounded values is the boundary
|
100
|
+
MAJOR_QUADRATIC = RoundingMajorMode.new(:MAJOR_QUADRATIC, :QUADRATIC, ALL_MINOR)
|
101
|
+
# the cubic mean of two adjacent rounded values is the boundary
|
102
|
+
MAJOR_CUBIC = RoundingMajorMode.new(:MAJOR_CUBIC, :CUBIC, ALL_MINOR)
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
ALL_MAJOR_MODES = [ MAJOR_UP, MAJOR_DOWN, MAJOR_CEILING, MAJOR_FLOOR, MAJOR_UNNECESSARY, MAJOR_HALF, MAJOR_GEOMETRIC, MAJOR_HARMONIC, MAJOR_QUADRATIC, MAJOR_CUBIC ]
|
107
|
+
|
108
|
+
|
109
|
+
MUL_INVERSE_MAJOR_MODE = {
|
110
|
+
MAJOR_UP => MAJOR_DOWN,
|
111
|
+
MAJOR_DOWN => MAJOR_UP,
|
112
|
+
MAJOR_CEILING => MAJOR_FLOOR,
|
113
|
+
MAJOR_FLOOR => MAJOR_CEILING,
|
114
|
+
MAJOR_HALF => MAJOR_HALF,
|
115
|
+
MAJOR_UNNECESSARY => MAJOR_UNNECESSARY
|
116
|
+
}
|
117
|
+
|
118
|
+
# which mode is to be used instead when we do an additive inversion?
|
119
|
+
ADD_INVERSE_MAJOR_MODE = {
|
120
|
+
MAJOR_UP => MAJOR_UP,
|
121
|
+
MAJOR_DOWN => MAJOR_DOWN,
|
122
|
+
MAJOR_CEILING => MAJOR_FLOOR,
|
123
|
+
MAJOR_FLOOR => MAJOR_CEILING,
|
124
|
+
MAJOR_HALF => MAJOR_HALF,
|
125
|
+
MAJOR_UNNECESSARY => MAJOR_UNNECESSARY
|
126
|
+
}
|
127
|
+
|
128
|
+
RoundingModeClass = Struct.new(:name, :major, :minor, :num)
|
30
129
|
|
31
130
|
#
|
32
131
|
# enumeration class to express the possible rounding modes that are
|
@@ -63,53 +162,281 @@ module LongDecimalRoundingMode
|
|
63
162
|
LongDecimalRoundingMode::ADD_INVERSE_MODE[self]
|
64
163
|
end
|
65
164
|
|
165
|
+
# internal use
|
166
|
+
# no special checks included
|
167
|
+
# we assume: lower <= unrounded <= upper
|
168
|
+
# we assume: sign = sgn(unrounded)
|
169
|
+
def pick_value(unrounded, sign, lower, upper, even)
|
170
|
+
if (sign == 0 || major == MAJOR_UNNECESSARY)
|
171
|
+
if (lower == unrounded)
|
172
|
+
return lower
|
173
|
+
elsif (upper == unrounded)
|
174
|
+
return upper
|
175
|
+
end
|
176
|
+
end
|
177
|
+
if (major == MAJOR_UP && sign > 0)
|
178
|
+
return upper
|
179
|
+
elsif (major == MAJOR_UP && sign < 0)
|
180
|
+
return lower
|
181
|
+
elsif (major == MAJOR_DOWN && sign > 0)
|
182
|
+
return lower
|
183
|
+
elsif (major == MAJOR_DOWN && sign < 0)
|
184
|
+
return upper
|
185
|
+
elsif (major == MAJOR_CEILING)
|
186
|
+
return upper
|
187
|
+
elsif (major == MAJOR_FLOOR)
|
188
|
+
return lower
|
189
|
+
elsif (major == MAJOR_UNNECESSARY)
|
190
|
+
raise ArgumentError, "rounding #{name} of unrounded=#{unrounded} (sign=#{sign}) is not applicable for lower=#{lower} and upper=#{upper}"
|
191
|
+
end
|
192
|
+
on_boundary = false
|
193
|
+
if (major == MAJOR_HALF)
|
194
|
+
d = unrounded - lower <=> upper - unrounded
|
195
|
+
if (d < 0)
|
196
|
+
# unrounded is below half
|
197
|
+
return lower
|
198
|
+
elsif (d > 0)
|
199
|
+
# unrounded is below half
|
200
|
+
return upper
|
201
|
+
else
|
202
|
+
on_boundary = true
|
203
|
+
end
|
204
|
+
elsif (major == MAJOR_GEOMETRIC)
|
205
|
+
prod = lower * upper
|
206
|
+
if (prod < 0)
|
207
|
+
raise ArgumentError, "geometric rounding #{name} of unrounded=#{unrounded} (sign=#{sign}) is not applicable for lower=#{lower} and upper=#{upper} with different signs"
|
208
|
+
elsif (prod == 0)
|
209
|
+
# lower or upper is 0
|
210
|
+
# we only round 0 to 0
|
211
|
+
if (sign == 0)
|
212
|
+
raise ArgumentError, "geometric rounding #{name} of unrounded=#{unrounded} (sign=#{sign}) is not applicable for lower=#{lower} and upper=#{upper} with 0 cannot be decided"
|
213
|
+
elsif (sign < 0)
|
214
|
+
return lower
|
215
|
+
else
|
216
|
+
return upper
|
217
|
+
end
|
218
|
+
end
|
219
|
+
# now prod > 0
|
220
|
+
square = unrounded * unrounded
|
221
|
+
d = square <=> prod
|
222
|
+
if (d < 0)
|
223
|
+
# |unrounded| < sqrt(lower*upper)
|
224
|
+
if (sign < 0)
|
225
|
+
# lower < unrounded < upper < 0
|
226
|
+
return upper
|
227
|
+
else
|
228
|
+
# (sign > 0)
|
229
|
+
return lower
|
230
|
+
end
|
231
|
+
elsif (d > 0)
|
232
|
+
# |unrounded| > sqrt(lower*upper)
|
233
|
+
if (sign < 0)
|
234
|
+
# lower < unrounded < upper < 0
|
235
|
+
return lower
|
236
|
+
else
|
237
|
+
# (sign > 0)
|
238
|
+
return upper
|
239
|
+
end
|
240
|
+
else
|
241
|
+
# (d == 0)
|
242
|
+
on_boundary = true
|
243
|
+
end
|
244
|
+
elsif (major == MAJOR_HARMONIC)
|
245
|
+
prod = lower * upper
|
246
|
+
if (prod < 0)
|
247
|
+
raise ArgumentError, "harmonic rounding #{name} of unrounded=#{unrounded} is not applicable for lower=#{lower} and upper=#{upper} with different signs"
|
248
|
+
elsif (prod == 0)
|
249
|
+
# lower or upper is 0
|
250
|
+
# we only round 0 to 0
|
251
|
+
if (sign == 0)
|
252
|
+
raise ArgumentError, "harmonic rounding #{name} of unrounded=#{unrounded} is not applicable for lower=#{lower} and upper=#{upper} with 0 cannot be decided"
|
253
|
+
elsif (sign < 0)
|
254
|
+
return lower
|
255
|
+
else
|
256
|
+
return upper
|
257
|
+
end
|
258
|
+
end
|
259
|
+
# now prod > 0
|
260
|
+
# so either lower < unrounded < upper < 0
|
261
|
+
# or 0 < lower < unrounded < upper
|
262
|
+
sum = lower + upper
|
263
|
+
lhs = unrounded * sum
|
264
|
+
rhs = 2*prod
|
265
|
+
d = lhs <=> rhs
|
266
|
+
if (sign < 0)
|
267
|
+
# lower + upper < 0
|
268
|
+
d = -d
|
269
|
+
end
|
270
|
+
if (d < 0)
|
271
|
+
# unrounded < 2*upper*lower/(upper+lower)
|
272
|
+
return lower
|
273
|
+
elsif (d > 0)
|
274
|
+
# unrounded > 2*upper*lower/(upper+lower)
|
275
|
+
return upper
|
276
|
+
else
|
277
|
+
# (d == 0)
|
278
|
+
on_boundary = true
|
279
|
+
end
|
280
|
+
elsif (major == MAJOR_QUADRATIC)
|
281
|
+
square = unrounded * unrounded
|
282
|
+
lhs = 2 * square
|
283
|
+
rhs = lower * lower + upper * upper
|
284
|
+
d = lhs <=> rhs
|
285
|
+
if (sign < 0)
|
286
|
+
# lower <= unrounded <= upper <= 0
|
287
|
+
# lower^2 >= unrounded >= upper^2 >= 0
|
288
|
+
d = -d
|
289
|
+
end
|
290
|
+
if (d < 0)
|
291
|
+
# unrounded < sqrt(...)
|
292
|
+
return lower
|
293
|
+
elsif (d > 0)
|
294
|
+
# unrounded > sqrt(...)
|
295
|
+
return upper
|
296
|
+
else
|
297
|
+
# (d == 0)
|
298
|
+
on_boundary = true
|
299
|
+
end
|
300
|
+
elsif (major == MAJOR_CUBIC)
|
301
|
+
cube = unrounded * unrounded * unrounded
|
302
|
+
lhs = 2 * cube
|
303
|
+
rhs = lower * lower * lower + upper * upper * upper
|
304
|
+
d = lhs <=> rhs
|
305
|
+
if (d < 0)
|
306
|
+
# unrounded < x_cubic(lower, upper)
|
307
|
+
return lower
|
308
|
+
elsif (d > 0)
|
309
|
+
# unrounded > x_cubic(lower, upper)
|
310
|
+
return upper
|
311
|
+
else
|
312
|
+
# (d == 0)
|
313
|
+
on_boundary = true
|
314
|
+
end
|
315
|
+
else
|
316
|
+
raise ArgumentError, "unsupported rounding mode (#{name}: major=#{major})"
|
317
|
+
end
|
318
|
+
if (! on_boundary)
|
319
|
+
raise ArgumentError, "rounding #{name} of unrounded=#{unrounded} failed for lower=#{lower} and upper=#{upper}: not on boundary"
|
320
|
+
end
|
321
|
+
if (minor == MINOR_UP)
|
322
|
+
return ROUND_UP.pick_value(unrounded, sign, lower, upper, even)
|
323
|
+
elsif (minor == MINOR_DOWN)
|
324
|
+
return ROUND_DOWN.pick_value(unrounded, sign, lower, upper, even)
|
325
|
+
elsif (minor == MINOR_CEILING)
|
326
|
+
return ROUND_CEILING.pick_value(unrounded, sign, lower, upper, even)
|
327
|
+
elsif (minor == MINOR_FLOOR)
|
328
|
+
return ROUND_FLOOR.pick_value(unrounded, sign, lower, upper, even)
|
329
|
+
elsif (minor == MINOR_UNUSED)
|
330
|
+
raise ArgumentError, "rounding #{name} of unrounded=#{unrounded} failed for lower=#{lower} and upper=#{upper}: on boundary but no applicable minor mode"
|
331
|
+
elsif (minor == MINOR_EVEN)
|
332
|
+
return even
|
333
|
+
elsif (minor == MINOR_ODD)
|
334
|
+
if (lower == even)
|
335
|
+
return upper
|
336
|
+
else
|
337
|
+
return lower
|
338
|
+
end
|
339
|
+
else
|
340
|
+
raise ArgumentError, "rounding #{name} of unrounded=#{unrounded} failed for lower=#{lower} and upper=#{upper}: on boundary but no applicable minor mode"
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
66
344
|
def hash
|
67
345
|
num
|
68
346
|
end
|
69
347
|
|
348
|
+
def to_long_s
|
349
|
+
"RM(#{name} major=#{major.name} minor=#{minor.name} num=#{num})"
|
350
|
+
end
|
351
|
+
|
352
|
+
def to_s
|
353
|
+
"#{name}"
|
354
|
+
end
|
355
|
+
|
70
356
|
end
|
71
357
|
|
358
|
+
MODE_LOOKUP = {}
|
359
|
+
|
360
|
+
rounding_mode_counter = 0
|
361
|
+
|
362
|
+
public
|
363
|
+
|
364
|
+
ALL_ROUNDING_MODES = []
|
365
|
+
|
366
|
+
MUL_INVERSE_MODE = {}
|
367
|
+
|
368
|
+
ADD_INVERSE_MODE = {}
|
369
|
+
|
370
|
+
|
371
|
+
ALL_MAJOR_MODES.each do |majm|
|
372
|
+
majm_str = majm.part.to_s
|
373
|
+
majm.minor.each do |minm|
|
374
|
+
minm_str = minm.part.to_s
|
375
|
+
const_str = "ROUND_" + majm_str + minm_str
|
376
|
+
class_eval("#{const_str} = RoundingModeClass.new(:#{const_str}, #{majm.name}, #{minm.name}, rounding_mode_counter)")
|
377
|
+
class_eval("#{const_str}.freeze")
|
378
|
+
rounding_mode_counter += 1
|
379
|
+
class_eval("ALL_ROUNDING_MODES.push(#{const_str})")
|
380
|
+
class_eval("MODE_LOOKUP[[#{majm.name}, #{minm.name}]] = #{const_str}")
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
ALL_ROUNDING_MODES.freeze
|
385
|
+
MODE_LOOKUP.freeze
|
386
|
+
|
387
|
+
ALL_ROUNDING_MODES.each do |rm|
|
388
|
+
majm = rm.major
|
389
|
+
minm = rm.minor
|
390
|
+
mul_inv = MODE_LOOKUP[[ MUL_INVERSE_MAJOR_MODE[majm], MUL_INVERSE_MINOR_MODE[minm]]]
|
391
|
+
MUL_INVERSE_MODE[rm] = mul_inv
|
392
|
+
add_inv = MODE_LOOKUP[[ ADD_INVERSE_MAJOR_MODE[majm], ADD_INVERSE_MINOR_MODE[minm]]]
|
393
|
+
ADD_INVERSE_MODE[rm] = add_inv
|
394
|
+
end
|
395
|
+
|
396
|
+
MUL_INVERSE_MODE.freeze
|
397
|
+
ADD_INVERSE_MODE.freeze
|
398
|
+
|
72
399
|
#
|
73
400
|
# rounding modes as constants
|
74
401
|
#
|
75
|
-
ROUND_UP = RoundingModeClass.new(:ROUND_UP, 0)
|
76
|
-
ROUND_DOWN = RoundingModeClass.new(:ROUND_DOWN, 1)
|
77
|
-
ROUND_CEILING = RoundingModeClass.new(:ROUND_CEILING, 2)
|
78
|
-
ROUND_FLOOR = RoundingModeClass.new(:ROUND_FLOOR, 3)
|
79
|
-
ROUND_HALF_UP = RoundingModeClass.new(:ROUND_HALF_UP, 4)
|
80
|
-
ROUND_HALF_DOWN = RoundingModeClass.new(:ROUND_HALF_DOWN, 5)
|
81
|
-
ROUND_HALF_CEILING = RoundingModeClass.new(:ROUND_HALF_CEILING, 6)
|
82
|
-
ROUND_HALF_FLOOR = RoundingModeClass.new(:ROUND_HALF_FLOOR, 7)
|
83
|
-
ROUND_HALF_EVEN = RoundingModeClass.new(:ROUND_HALF_EVEN, 8)
|
84
|
-
ROUND_UNNECESSARY = RoundingModeClass.new(:ROUND_UNNECESSARY, 9)
|
85
|
-
|
86
|
-
# which mode is to be used instead when we do an multiplicative inversion?
|
87
|
-
MUL_INVERSE_MODE = {
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
}
|
99
|
-
|
100
|
-
# which mode is to be used instead when we do an additive inversion?
|
101
|
-
ADD_INVERSE_MODE = {
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
}
|
402
|
+
# ROUND_UP = RoundingModeClass.new(:ROUND_UP, 0)
|
403
|
+
# ROUND_DOWN = RoundingModeClass.new(:ROUND_DOWN, 1)
|
404
|
+
# ROUND_CEILING = RoundingModeClass.new(:ROUND_CEILING, 2)
|
405
|
+
# ROUND_FLOOR = RoundingModeClass.new(:ROUND_FLOOR, 3)
|
406
|
+
# ROUND_HALF_UP = RoundingModeClass.new(:ROUND_HALF_UP, 4)
|
407
|
+
# ROUND_HALF_DOWN = RoundingModeClass.new(:ROUND_HALF_DOWN, 5)
|
408
|
+
# ROUND_HALF_CEILING = RoundingModeClass.new(:ROUND_HALF_CEILING, 6)
|
409
|
+
# ROUND_HALF_FLOOR = RoundingModeClass.new(:ROUND_HALF_FLOOR, 7)
|
410
|
+
# ROUND_HALF_EVEN = RoundingModeClass.new(:ROUND_HALF_EVEN, 8)
|
411
|
+
# ROUND_UNNECESSARY = RoundingModeClass.new(:ROUND_UNNECESSARY, 9)
|
412
|
+
|
413
|
+
# # which mode is to be used instead when we do an multiplicative inversion?
|
414
|
+
# MUL_INVERSE_MODE = {
|
415
|
+
# ROUND_UP => ROUND_DOWN,
|
416
|
+
# ROUND_DOWN => ROUND_UP,
|
417
|
+
# ROUND_CEILING => ROUND_FLOOR,
|
418
|
+
# ROUND_FLOOR => ROUND_CEILING,
|
419
|
+
# ROUND_HALF_UP => ROUND_HALF_DOWN,
|
420
|
+
# ROUND_HALF_DOWN => ROUND_HALF_UP,
|
421
|
+
# ROUND_HALF_CEILING => ROUND_HALF_FLOOR,
|
422
|
+
# ROUND_HALF_FLOOR => ROUND_HALF_CEILING,
|
423
|
+
# ROUND_HALF_EVEN => ROUND_HALF_EVEN,
|
424
|
+
# ROUND_UNNECESSARY => ROUND_UNNECESSARY
|
425
|
+
# }
|
426
|
+
|
427
|
+
# # which mode is to be used instead when we do an additive inversion?
|
428
|
+
# ADD_INVERSE_MODE = {
|
429
|
+
# ROUND_UP => ROUND_UP,
|
430
|
+
# ROUND_DOWN => ROUND_DOWN,
|
431
|
+
# ROUND_CEILING => ROUND_FLOOR,
|
432
|
+
# ROUND_FLOOR => ROUND_CEILING,
|
433
|
+
# ROUND_HALF_UP => ROUND_HALF_UP,
|
434
|
+
# ROUND_HALF_DOWN => ROUND_HALF_DOWN,
|
435
|
+
# ROUND_HALF_CEILING => ROUND_HALF_FLOOR,
|
436
|
+
# ROUND_HALF_FLOOR => ROUND_HALF_CEILING,
|
437
|
+
# ROUND_HALF_EVEN => ROUND_HALF_EVEN,
|
438
|
+
# ROUND_UNNECESSARY => ROUND_UNNECESSARY
|
439
|
+
# }
|
113
440
|
|
114
441
|
ZeroRoundingModeClass = Struct.new(:name, :num)
|
115
442
|
|
@@ -138,6 +465,14 @@ module LongDecimalRoundingMode
|
|
138
465
|
num
|
139
466
|
end
|
140
467
|
|
468
|
+
def to_long_s
|
469
|
+
"ZM(#{name} num=#{num})"
|
470
|
+
end
|
471
|
+
|
472
|
+
def to_s
|
473
|
+
"#{name}"
|
474
|
+
end
|
475
|
+
|
141
476
|
end
|
142
477
|
|
143
478
|
#
|
@@ -149,6 +484,12 @@ module LongDecimalRoundingMode
|
|
149
484
|
ZERO_ROUND_TO_CLOSEST_PREFER_MINUS = ZeroRoundingModeClass.new(:ZERO_ROUND_TO_CLOSEST_PREFER_MINUS, 3)
|
150
485
|
ZERO_ROUND_UNNECESSARY = ZeroRoundingModeClass.new(:ZERO_ROUND_UNNECESSARY, 4)
|
151
486
|
|
487
|
+
ALL_ZERO_MODES = [ LongDecimalRoundingMode::ZERO_ROUND_TO_PLUS,\
|
488
|
+
LongDecimalRoundingMode::ZERO_ROUND_TO_MINUS,\
|
489
|
+
LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_PLUS,\
|
490
|
+
LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_MINUS,\
|
491
|
+
LongDecimalRoundingMode::ZERO_ROUND_UNNECESSARY ];
|
492
|
+
|
152
493
|
end # LongDecimalRoundingMode
|
153
494
|
|
154
495
|
# JRuby has a bug to be fixed in version > 1.2 that implies results of
|
@@ -185,6 +526,8 @@ if (RUBY_PLATFORM == 'java')
|
|
185
526
|
end
|
186
527
|
end
|
187
528
|
|
529
|
+
|
530
|
+
|
188
531
|
#
|
189
532
|
# add one method to Integer
|
190
533
|
#
|
@@ -221,23 +564,25 @@ class Integer
|
|
221
564
|
# use, zero_rounding_mode has to be used
|
222
565
|
# to decide.
|
223
566
|
#
|
224
|
-
def round_to_allowed_remainders(
|
567
|
+
def round_to_allowed_remainders(remainders_param,
|
225
568
|
modulus,
|
226
569
|
rounding_mode = LongDecimalRoundingMode::ROUND_UNNECESSARY,
|
227
570
|
zero_rounding_mode = LongDecimalRoundingMode::ZERO_ROUND_UNNECESSARY)
|
228
571
|
|
229
|
-
raise TypeError, "remainders must be Array" unless
|
230
|
-
raise TypeError, "remainders must be non-empty Array" unless
|
572
|
+
raise TypeError, "remainders must be Array" unless remainders_param.kind_of? Array
|
573
|
+
raise TypeError, "remainders must be non-empty Array" unless remainders_param.length > 0
|
231
574
|
raise TypeError, "modulus #{modulus.inspect} must be integer" unless modulus.kind_of? Integer
|
232
575
|
raise TypeError, "modulus #{modulus.inspect} must be >= 2" unless modulus >= 2
|
233
576
|
raise TypeError, "rounding_mode #{rounding_mode.inspect} must be legal rounding rounding_mode" unless rounding_mode.kind_of? LongDecimalRoundingMode::RoundingModeClass
|
234
|
-
raise TypeError, "
|
577
|
+
raise TypeError, "#{rounding_mode} is not applicable here" if rounding_mode.minor == LongDecimalRoundingMode::MINOR_EVEN
|
578
|
+
raise TypeError, "#{rounding_mode} is not applicable here" if rounding_mode.minor == LongDecimalRoundingMode::MINOR_ODD
|
235
579
|
raise TypeError, "zero_rounding_mode #{zero_rounding_mode.inspect} must be legal zero_rounding zero_rounding_mode" unless zero_rounding_mode.kind_of? LongDecimalRoundingMode::ZeroRoundingModeClass
|
236
580
|
|
581
|
+
remainders = remainders_param.clone
|
237
582
|
r_self = self % modulus
|
238
583
|
r_self_00 = r_self
|
239
584
|
remainders = remainders.collect do |r|
|
240
|
-
raise TypeError, "remainders must be numbers" unless r.kind_of? Integer
|
585
|
+
raise TypeError, "remainders must be integral numbers" unless r.kind_of? Integer
|
241
586
|
r % modulus
|
242
587
|
end
|
243
588
|
remainders.sort!.uniq!
|
@@ -261,45 +606,79 @@ class Integer
|
|
261
606
|
break
|
262
607
|
end
|
263
608
|
end
|
609
|
+
if (r_lower < 0)
|
610
|
+
raise ArgumentError, "self=#{self} r_self=#{r_self} r_lower=#{r_lower} r_upper=#{r_upper}"
|
611
|
+
end
|
612
|
+
if (r_upper < 0)
|
613
|
+
raise ArgumentError, "self=#{self} r_self=#{r_self} r_lower=#{r_lower} r_upper=#{r_upper}"
|
614
|
+
end
|
264
615
|
lower = self - (r_self - r_lower)
|
265
616
|
upper = self + (r_upper - r_self)
|
266
617
|
|
267
618
|
unless (lower < self && self < upper)
|
268
|
-
raise ArgumentError, "self=#{self} not in (#{lower}, #{upper})"
|
619
|
+
raise ArgumentError, "self=#{self} not in (#{lower}, #{upper}) with r_self=#{r_self} r_lower=#{r_lower} r_upper=#{r_upper}"
|
269
620
|
end
|
270
621
|
if (rounding_mode == LongDecimalRoundingMode::ROUND_UNNECESSARY) then
|
271
622
|
raise ArgumentError, "mode ROUND_UNNECESSARY not applicable, self=#{self.to_s} is in open interval (#{lower}, #{upper})"
|
272
623
|
end
|
273
624
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
625
|
+
if (rounding_mode == LongDecimalRoundingMode::ROUND_FLOOR) then
|
626
|
+
if (lower.nil?)
|
627
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
628
|
+
end
|
629
|
+
return lower
|
630
|
+
elsif (rounding_mode == LongDecimalRoundingMode::ROUND_CEILING) then
|
631
|
+
if (upper.nil?)
|
632
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
633
|
+
end
|
634
|
+
return upper
|
635
|
+
end
|
279
636
|
|
280
|
-
sign_self = self.
|
637
|
+
sign_self = self.sgn
|
281
638
|
if (sign_self == 0) then
|
282
|
-
if (rounding_mode == LongDecimalRoundingMode::ROUND_UP
|
283
|
-
||
|
639
|
+
if (rounding_mode == LongDecimalRoundingMode::ROUND_UP \
|
640
|
+
|| rounding_mode == LongDecimalRoundingMode::ROUND_DOWN \
|
641
|
+
|| rounding_mode.major == LongDecimalRoundingMode::MAJOR_GEOMETRIC \
|
642
|
+
|| rounding_mode.major == LongDecimalRoundingMode::MAJOR_HARMONIC \
|
643
|
+
|| rounding_mode.major == LongDecimalRoundingMode::MAJOR_QUADRATIC \
|
644
|
+
|| lower == -upper && (rounding_mode.minor == LongDecimalRoundingMode::MINOR_UP || rounding_mode.minor == LongDecimalRoundingMode::MINOR_DOWN))
|
284
645
|
if (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_UNNECESSARY) then
|
285
|
-
raise ArgumentError, "self=#{self.to_s} is 0 in open interval (#{lower}, #{upper}) and cannot be resolved with ZERO_ROUND_UNNECESSARY"
|
646
|
+
raise ArgumentError, "self=#{self.to_s} is 0 in open interval (#{lower}, #{upper}) and cannot be resolved with ZERO_ROUND_UNNECESSARY (rounding_mode=#{rounding_mode} modulus=#{modulus} remainders=#{remainders}"
|
286
647
|
elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_PLUS \
|
287
648
|
|| zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_MINUS) then
|
288
649
|
diff = lower.abs <=> upper.abs
|
289
650
|
if (diff < 0) then
|
651
|
+
if (lower.nil?)
|
652
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
653
|
+
end
|
290
654
|
return lower
|
291
655
|
elsif (diff > 0) then
|
656
|
+
if (upper.nil?)
|
657
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
658
|
+
end
|
292
659
|
return upper
|
293
660
|
elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_PLUS) then
|
661
|
+
if (upper.nil?)
|
662
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
663
|
+
end
|
294
664
|
return upper
|
295
665
|
elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_CLOSEST_PREFER_MINUS) then
|
666
|
+
if (lower.nil?)
|
667
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
668
|
+
end
|
296
669
|
return lower
|
297
670
|
else
|
298
671
|
raise ArgumentError, "this case can never happen: zero_rounding_mode=#{zero_rounding_mode}"
|
299
672
|
end
|
300
673
|
elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_PLUS) then
|
674
|
+
if (upper.nil?)
|
675
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
676
|
+
end
|
301
677
|
return upper
|
302
678
|
elsif (zero_rounding_mode == LongDecimalRoundingMode::ZERO_ROUND_TO_MINUS) then
|
679
|
+
if (lower.nil?)
|
680
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
681
|
+
end
|
303
682
|
return lower
|
304
683
|
else
|
305
684
|
raise ArgumentError, "this case can never happen: zero_rounding_mode=#{zero_rounding_mode}"
|
@@ -307,45 +686,66 @@ class Integer
|
|
307
686
|
end
|
308
687
|
end
|
309
688
|
|
310
|
-
#
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
# ROUND_DOWN goes to the closest possible value towards zero or beyond zero
|
317
|
-
rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_CEILING : LongDecimalRoundingMode::ROUND_FLOOR
|
318
|
-
elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_UP)
|
319
|
-
# ROUND_HALF_UP goes to the closest possible value preferring away from zero
|
320
|
-
rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_HALF_FLOOR : LongDecimalRoundingMode::ROUND_HALF_CEILING
|
321
|
-
elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_DOWN)
|
322
|
-
# ROUND_HALF_DOWN goes to the closest possible value preferring towards zero or beyond zero
|
323
|
-
rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_HALF_CEILING : LongDecimalRoundingMode::ROUND_HALF_FLOOR
|
324
|
-
end
|
325
|
-
if (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_FLOOR \
|
326
|
-
|| rounding_mode == LongDecimalRoundingMode::ROUND_HALF_CEILING) then
|
327
|
-
d_lower = self - lower
|
328
|
-
d_upper = upper - self
|
329
|
-
if (d_lower < d_upper) then
|
330
|
-
return lower
|
331
|
-
elsif (d_upper < d_lower) then
|
689
|
+
# for geometric and harmonic never round across 0
|
690
|
+
if (rounding_mode.major == LongDecimalRoundingMode::MAJOR_GEOMETRIC || rounding_mode.major == LongDecimalRoundingMode::MAJOR_HARMONIC || rounding_mode.major == LongDecimalRoundingMode::MAJOR_QUADRATIC)
|
691
|
+
if (sign_self > 0 && lower < 0)
|
692
|
+
if (upper.nil?)
|
693
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
694
|
+
end
|
332
695
|
return upper
|
333
|
-
elsif (
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
raise ArgumentError, "this case can never happen: rounding_mode=#{rounding_mode}"
|
696
|
+
elsif (sign_self < 0 && upper > 0)
|
697
|
+
if (lower.nil?)
|
698
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
699
|
+
end
|
700
|
+
return lower
|
339
701
|
end
|
340
702
|
end
|
341
703
|
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
704
|
+
# now we can assume that sign_self (and self) is != 0, which allows to decide on the rounding_mode
|
705
|
+
pick = rounding_mode.pick_value(self, sign_self, lower, upper, nil)
|
706
|
+
# if (rounding_mode == LongDecimalRoundingMode::ROUND_UP)
|
707
|
+
# # ROUND_UP goes to the closest possible value away from zero
|
708
|
+
# rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_FLOOR : LongDecimalRoundingMode::ROUND_CEILING
|
709
|
+
# elsif (rounding_mode == LongDecimalRoundingMode::ROUND_DOWN)
|
710
|
+
# # ROUND_DOWN goes to the closest possible value towards zero or beyond zero
|
711
|
+
# rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_CEILING : LongDecimalRoundingMode::ROUND_FLOOR
|
712
|
+
# elsif (rounding_mode.minor == LongDecimalRoundingMode::MINOR_UP)
|
713
|
+
# # ROUND_*_UP goes to the closest possible value preferring away from zero
|
714
|
+
# rounding_mode_minor = (sign_self < 0) ? LongDecimalRoundingMode::MINOR_FLOOR : LongDecimalRoundingMode::MINOR_CEILING
|
715
|
+
# rounding_mode = LongDecimalRoundingMode::MODE_LOOKUP[[rounding_mode.major, rounding_mode_minor]]
|
716
|
+
# elsif (rounding_mode.minor == LongDecimalRoundingMode::MINOR_DOWN)
|
717
|
+
# # ROUND_*_DOWN goes to the closest possible value preferring towards zero or beyond zero
|
718
|
+
# rounding_mode_minor = (sign_self < 0) ? LongDecimalRoundingMode::MINOR_CEILING : LongDecimalRoundingMode::MINOR_FLOOR
|
719
|
+
# rounding_mode = LongDecimalRoundingMode::MODE_LOOKUP[[rounding_mode.major, rounding_mode_minor]]
|
720
|
+
# end
|
721
|
+
# if (rounding_mode.minor == LongDecimalRoundingMode::MINOR_FLOOR \
|
722
|
+
# || rounding_mode.minor == LongDecimalRoundingMode::MINOR_CEILING) then
|
723
|
+
# d_lower = self - lower
|
724
|
+
# d_upper = upper - self
|
725
|
+
# if (d_lower < d_upper) then
|
726
|
+
# return lower
|
727
|
+
# elsif (d_upper < d_lower) then
|
728
|
+
# return upper
|
729
|
+
# elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_FLOOR) then
|
730
|
+
# rounding_mode = LongDecimalRoundingMode::ROUND_FLOOR
|
731
|
+
# elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_CEILING) then
|
732
|
+
# rounding_mode = LongDecimalRoundingMode::ROUND_CEILING
|
733
|
+
# else
|
734
|
+
# raise ArgumentError, "this case can never happen: rounding_mode=#{rounding_mode}"
|
735
|
+
# end
|
736
|
+
# end
|
737
|
+
|
738
|
+
# if (rounding_mode == LongDecimalRoundingMode::ROUND_FLOOR) then
|
739
|
+
# return lower
|
740
|
+
# elsif (rounding_mode == LongDecimalRoundingMode::ROUND_CEILING) then
|
741
|
+
# return upper
|
742
|
+
# else
|
743
|
+
# raise ArgumentError, "this case can never happen: rounding_mode=#{rounding_mode}"
|
744
|
+
# end
|
745
|
+
if (pick.nil?)
|
746
|
+
raise ArgumentError, "remainders=#{remainders} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}"
|
747
|
+
end
|
748
|
+
return pick
|
349
749
|
end
|
350
750
|
end
|
351
751
|
|
@@ -353,7 +753,7 @@ end
|
|
353
753
|
# common base class for LongDecimal and LongDecimalQuot
|
354
754
|
#
|
355
755
|
class LongDecimalBase < Numeric
|
356
|
-
@@RCS_ID='-$Id: long-decimal.rb,v 1.
|
756
|
+
@@RCS_ID='-$Id: long-decimal.rb,v 1.87 2011/01/30 20:01:40 bk1 Exp $-'
|
357
757
|
|
358
758
|
# allow easy check if running with version 1.9
|
359
759
|
RUNNING_19 = RUBY_VERSION.match /^1\.9/
|
@@ -476,7 +876,53 @@ class LongDecimalBase < Numeric
|
|
476
876
|
return LongDecimal(0, new_scale)
|
477
877
|
end
|
478
878
|
|
479
|
-
|
879
|
+
quot, rem = dividend.divmod(divisor)
|
880
|
+
sign_rem = rem <=> 0
|
881
|
+
if (sign_rem == 0)
|
882
|
+
# if self can be expressed without loss as LongDecimal with
|
883
|
+
# new_scale digits after the decimal point, just do it.
|
884
|
+
return LongDecimal(quot, new_scale)
|
885
|
+
end
|
886
|
+
|
887
|
+
# we do not expect negative signs of remainder. To make sure that
|
888
|
+
# this does not cause problems in further code, we just throw an
|
889
|
+
# exception. This should never happen (and did not happen during
|
890
|
+
# testing).
|
891
|
+
raise ArgumentError, "signs do not match self=#{self.to_s} f=#{factor} dividend=#{dividend} divisor=#{divisor} quot=#{quot} rem=#{rem}" if sign_rem <= 0
|
892
|
+
|
893
|
+
# now we have
|
894
|
+
# dividend == quot * divisor + rem
|
895
|
+
# where 0 < rem < quot (the case rem == 0 has already been handled
|
896
|
+
# so
|
897
|
+
# quot < divisor/dividend < quot+1
|
898
|
+
lower = quot
|
899
|
+
upper = quot+1
|
900
|
+
even = if (mode.minor == MINOR_EVEN || mode.minor == MINOR_ODD)
|
901
|
+
even = if (lower[0] == 1)
|
902
|
+
upper
|
903
|
+
else
|
904
|
+
lower
|
905
|
+
end
|
906
|
+
else
|
907
|
+
nil
|
908
|
+
end
|
909
|
+
value = mode.pick_value(Rational(dividend, divisor), sign_quot, lower, upper, even)
|
910
|
+
LongDecimal(value, new_scale)
|
911
|
+
|
912
|
+
end
|
913
|
+
|
914
|
+
#
|
915
|
+
# helper method for round_to_scale
|
916
|
+
# will be replaced by shorter implementation
|
917
|
+
#
|
918
|
+
def round_to_scale_helper_old(dividend, divisor, new_scale, mode)
|
919
|
+
|
920
|
+
sign_quot = dividend <=> 0
|
921
|
+
if sign_quot == 0 then
|
922
|
+
# finish zero without long calculations at once
|
923
|
+
return LongDecimal(0, new_scale)
|
924
|
+
end
|
925
|
+
|
480
926
|
quot, rem = dividend.divmod(divisor)
|
481
927
|
sign_rem = rem <=> 0
|
482
928
|
if (sign_rem == 0)
|
@@ -523,33 +969,35 @@ class LongDecimalBase < Numeric
|
|
523
969
|
|
524
970
|
else
|
525
971
|
|
526
|
-
if (mode ==
|
972
|
+
if (mode.minor == MINOR_CEILING)
|
527
973
|
# ROUND_HALF_CEILING goes to the closest allowed number >= self, even
|
528
974
|
# for negative numbers. Since sign is handled separately, it is
|
529
975
|
# more conveniant to use ROUND_HALF_UP or ROUND_HALF_DOWN depending on the
|
530
976
|
# sign.
|
531
|
-
|
532
|
-
|
533
|
-
elsif (mode ==
|
977
|
+
minor_mode = (sign_quot > 0) ? MINOR_UP : MINOR_DOWN
|
978
|
+
mode = MODE_LOOKUP[[mode.major, minor_mode]]
|
979
|
+
elsif (mode.minor == MINOR_FLOOR)
|
534
980
|
# ROUND_HALF_FLOOR goes to the closest allowed number <= self, even
|
535
981
|
# for negative numbers. Since sign is handled separately, it is
|
536
982
|
# more conveniant to use ROUND_HALF_UP or ROUND_HALF_DOWN depending on the
|
537
983
|
# sign.
|
538
|
-
|
539
|
-
|
984
|
+
minor_mode = (sign_quot < 0) ? MINOR_UP : MINOR_DOWN
|
985
|
+
mode = MODE_LOOKUP[[mode.major, minor_mode]]
|
540
986
|
end
|
541
987
|
|
542
988
|
# handle the ROUND_HALF_... stuff and find the adequate ROUND_UP
|
543
989
|
# or ROUND_DOWN to use
|
544
990
|
abs_rem = rem.abs
|
545
|
-
|
546
|
-
if (mode ==
|
547
|
-
|
991
|
+
direction_criteria = nil
|
992
|
+
if (mode.major == MAJOR_HALF)
|
993
|
+
direction_criteria = (abs_rem << 1) <=> divisor
|
994
|
+
|
995
|
+
if (direction_criteria < 0) then
|
548
996
|
mode = ROUND_DOWN
|
549
|
-
elsif
|
997
|
+
elsif direction_criteria > 0 then
|
550
998
|
mode = ROUND_UP
|
551
999
|
else
|
552
|
-
#
|
1000
|
+
# direction_criteria == 0
|
553
1001
|
if (mode == ROUND_HALF_UP) then
|
554
1002
|
mode = ROUND_UP
|
555
1003
|
elsif (mode == ROUND_HALF_DOWN) then
|
@@ -583,7 +1031,7 @@ end # class LongDecimalBase
|
|
583
1031
|
# digits and the other one the position of the decimal point.
|
584
1032
|
#
|
585
1033
|
class LongDecimal < LongDecimalBase
|
586
|
-
@@RCS_ID='-$Id: long-decimal.rb,v 1.
|
1034
|
+
@@RCS_ID='-$Id: long-decimal.rb,v 1.87 2011/01/30 20:01:40 bk1 Exp $-'
|
587
1035
|
|
588
1036
|
# MINUS_ONE = LongDecimal(-1)
|
589
1037
|
# ZERO = LongDecimal(0)
|
@@ -731,7 +1179,7 @@ class LongDecimal < LongDecimalBase
|
|
731
1179
|
raise TypeError, "1st arg must not be empty string. \"#{num_str.inspect}\"" if len == 0
|
732
1180
|
|
733
1181
|
# remove spaces and underscores
|
734
|
-
num_str.gsub
|
1182
|
+
num_str = num_str.gsub(/\s/, "")
|
735
1183
|
num_str.gsub!(/_/, "")
|
736
1184
|
|
737
1185
|
# handle sign
|
@@ -894,6 +1342,7 @@ class LongDecimal < LongDecimalBase
|
|
894
1342
|
raise TypeError, "modulus #{modulus.inspect} must be >= 2" unless modulus >= 2
|
895
1343
|
raise TypeError, "rounding_mode #{rounding_mode.inspect} must be legal rounding rounding_mode" unless rounding_mode.kind_of? RoundingModeClass
|
896
1344
|
raise TypeError, "ROUND_HALF_EVEN is not applicable here" if rounding_mode == LongDecimalRoundingMode::ROUND_HALF_EVEN
|
1345
|
+
raise TypeError, "ROUND_HALF_ODD is not applicable here" if rounding_mode == LongDecimalRoundingMode::ROUND_HALF_ODD
|
897
1346
|
raise TypeError, "zero_rounding_mode #{zero_rounding_mode.inspect} must be legal zero_rounding zero_rounding_mode" unless zero_rounding_mode.kind_of? ZeroRoundingModeClass
|
898
1347
|
|
899
1348
|
if @scale < new_scale then
|
@@ -991,7 +1440,6 @@ class LongDecimal < LongDecimalBase
|
|
991
1440
|
# result 0.0 if int_val == 0
|
992
1441
|
if (int_val == 0)
|
993
1442
|
# t1
|
994
|
-
# puts "t1 #{self.to_s}=#{self.inspect} -> 0.0"
|
995
1443
|
return 0.0
|
996
1444
|
end
|
997
1445
|
|
@@ -999,15 +1447,12 @@ class LongDecimal < LongDecimalBase
|
|
999
1447
|
if (int_val.abs <= LongMath::MAX_FLOATABLE)
|
1000
1448
|
y = int_val.to_f
|
1001
1449
|
# t2
|
1002
|
-
# puts "t2 #{self.to_s}=#{self.inspect} -> #{y}"
|
1003
1450
|
return y
|
1004
1451
|
elsif int_val < 0
|
1005
1452
|
# t13
|
1006
|
-
# puts "t13 #{self.to_s}=#{self.inspect} -> -Infinity"
|
1007
1453
|
return -1.0/0.0
|
1008
1454
|
elsif int_val > 0
|
1009
1455
|
# t14
|
1010
|
-
# puts "t13 #{self.to_s}=#{self.inspect} -> Infinity"
|
1011
1456
|
return 1.0/0.0
|
1012
1457
|
end
|
1013
1458
|
end
|
@@ -1016,7 +1461,6 @@ class LongDecimal < LongDecimalBase
|
|
1016
1461
|
if (self < 0) then
|
1017
1462
|
y = -(-self).to_f
|
1018
1463
|
# t3
|
1019
|
-
# puts "t3 #{self.to_s}=#{self.inspect} -> #{y}"
|
1020
1464
|
return y
|
1021
1465
|
end
|
1022
1466
|
|
@@ -1024,7 +1468,6 @@ class LongDecimal < LongDecimalBase
|
|
1024
1468
|
if (int_val <= LongMath::MAX_FLOATABLE && scale <= Float::MAX_10_EXP)
|
1025
1469
|
y = to_f_simple(int_val, scale)
|
1026
1470
|
# t4
|
1027
|
-
# puts "t4 #{self.to_s}=#{self.inspect} -> #{y}"
|
1028
1471
|
return y
|
1029
1472
|
end
|
1030
1473
|
|
@@ -1034,7 +1477,6 @@ class LongDecimal < LongDecimalBase
|
|
1034
1477
|
# handle overflow: raise exception
|
1035
1478
|
if (int_val > divisor * LongMath::MAX_FLOATABLE) then
|
1036
1479
|
# t5
|
1037
|
-
# puts "t5 #{self.to_s}=#{self.inspect} -> Infinity"
|
1038
1480
|
return 1/0.0 # Infinity
|
1039
1481
|
end
|
1040
1482
|
|
@@ -1042,7 +1484,6 @@ class LongDecimal < LongDecimalBase
|
|
1042
1484
|
# handle underflow: return 0.0
|
1043
1485
|
if (int_val * LongMath::INV_MIN_FLOATABLE * 20 < divisor) then
|
1044
1486
|
# t6
|
1045
|
-
# puts "t6 #{self.to_s}=#{self.inspect} -> 0.0"
|
1046
1487
|
p = int_val * LongMath::INV_MIN_FLOATABLE * 20
|
1047
1488
|
d = divisor
|
1048
1489
|
n = int_val
|
@@ -1062,13 +1503,11 @@ class LongDecimal < LongDecimalBase
|
|
1062
1503
|
y *= q
|
1063
1504
|
if (y == 0.0)
|
1064
1505
|
# t7
|
1065
|
-
# puts "t7 #{self.to_s}=#{self.inspect} -> #{y}"
|
1066
1506
|
return y
|
1067
1507
|
end
|
1068
1508
|
s -= qe
|
1069
1509
|
end
|
1070
1510
|
# t8
|
1071
|
-
# puts "t8 #{self.to_s}=#{self.inspect} -> #{y}"
|
1072
1511
|
return y
|
1073
1512
|
end
|
1074
1513
|
|
@@ -1081,7 +1520,6 @@ class LongDecimal < LongDecimalBase
|
|
1081
1520
|
# scale = 0, 0 < int_val <= MAX_FLOATABLE
|
1082
1521
|
y = to_f_simple(rounded_ld.int_val, rounded_ld.scale)
|
1083
1522
|
# t9
|
1084
|
-
# puts "t9 #{self.to_s}=#{self.inspect} #{rounded_ld.to_s}=#{rounded_ld.inspect} -> #{y}"
|
1085
1523
|
return y
|
1086
1524
|
end
|
1087
1525
|
|
@@ -1092,7 +1530,6 @@ class LongDecimal < LongDecimalBase
|
|
1092
1530
|
|
1093
1531
|
if (cmp == 0)
|
1094
1532
|
# t10
|
1095
|
-
# puts "t10 #{self.to_s}=#{self.inspect} -> 1.0"
|
1096
1533
|
return 1.0
|
1097
1534
|
elsif (cmp > 0)
|
1098
1535
|
# self > 1, retain MAX_SIGNIFICANT_FLOATABLE_DIGITS
|
@@ -1100,7 +1537,6 @@ class LongDecimal < LongDecimalBase
|
|
1100
1537
|
# self >= LongMath::FLOATABLE_WITHOUT_FRACTION > self > 1, scale = 16,
|
1101
1538
|
y = to_f_simple(rounded_ld.int_val, rounded_ld.scale)
|
1102
1539
|
# t11
|
1103
|
-
# puts "t11 #{self.to_s}=#{self.inspect} #{rounded_ld.to_s}=#{rounded_ld.inspect} -> #{y}"
|
1104
1540
|
return y
|
1105
1541
|
else
|
1106
1542
|
# self < 1
|
@@ -1116,7 +1552,6 @@ class LongDecimal < LongDecimalBase
|
|
1116
1552
|
rounded_ld = round_to_scale(new_scale, ROUND_HALF_UP)
|
1117
1553
|
y = to_f_simple(rounded_ld.int_val, rounded_ld.scale)
|
1118
1554
|
# t12
|
1119
|
-
# puts "t12 #{self.to_s}=#{self.inspect} #{rounded_ld.to_s}=#{rounded_ld.inspect} sd=#{sd} #{self <=> 1} -> #{y}"
|
1120
1555
|
return y
|
1121
1556
|
end
|
1122
1557
|
end
|
@@ -1756,11 +2191,9 @@ class LongDecimal < LongDecimalBase
|
|
1756
2191
|
# s, o = other.coerce(self.to_f)
|
1757
2192
|
if (RUNNING_19)
|
1758
2193
|
s, o = other.coerce(self)
|
1759
|
-
# puts "complex coerce 19: #{self}, #{other} -> #{s}, #{o}"
|
1760
2194
|
return o, s
|
1761
2195
|
else
|
1762
2196
|
s, o = other.coerce(Complex(self, 0))
|
1763
|
-
# puts "complex coerce 18/J: #{self}, #{other} -> #{s}, #{o}"
|
1764
2197
|
return o, s
|
1765
2198
|
end
|
1766
2199
|
# s, o = other.coerce(Complex(self.to_f, 0))
|
@@ -1880,7 +2313,7 @@ end # LongDecimal
|
|
1880
2313
|
#
|
1881
2314
|
class LongDecimalQuot < LongDecimalBase
|
1882
2315
|
|
1883
|
-
@@RCS_ID='-$Id: long-decimal.rb,v 1.
|
2316
|
+
@@RCS_ID='-$Id: long-decimal.rb,v 1.87 2011/01/30 20:01:40 bk1 Exp $-'
|
1884
2317
|
|
1885
2318
|
#
|
1886
2319
|
# constructor
|
@@ -1924,7 +2357,7 @@ class LongDecimalQuot < LongDecimalBase
|
|
1924
2357
|
# digits before the decimal point. Since division is usually
|
1925
2358
|
# not expressable exactly in decimal digits, it is up to the
|
1926
2359
|
# calling application to decide on the number of digits actually
|
1927
|
-
# used for the result, which can be more
|
2360
|
+
# used for the result, which can be more than new_scale.
|
1928
2361
|
sx = first.scale
|
1929
2362
|
sy = second.scale
|
1930
2363
|
dx = first.sint_digits10
|
@@ -2096,7 +2529,6 @@ class LongDecimalQuot < LongDecimalBase
|
|
2096
2529
|
if (s.kind_of? LongDecimalQuot) then
|
2097
2530
|
LongDecimalQuot(s.rat - o.rat, [s.scale, o.scale].max)
|
2098
2531
|
else
|
2099
|
-
# puts "ldq-coerce: s=#{s} o=#{o} self=#{self} other=#{other}"
|
2100
2532
|
s - o
|
2101
2533
|
end
|
2102
2534
|
end
|
@@ -2386,6 +2818,17 @@ class Numeric
|
|
2386
2818
|
(self - 1).zero?
|
2387
2819
|
end
|
2388
2820
|
|
2821
|
+
def sgn
|
2822
|
+
raw_sign = self <=> 0
|
2823
|
+
if (raw_sign < 0)
|
2824
|
+
-1
|
2825
|
+
elsif (raw_sign == 0)
|
2826
|
+
0
|
2827
|
+
else
|
2828
|
+
+1
|
2829
|
+
end
|
2830
|
+
end
|
2831
|
+
|
2389
2832
|
end # Numeric
|
2390
2833
|
|
2391
2834
|
class Complex
|
@@ -2492,8 +2935,8 @@ class Rational
|
|
2492
2935
|
|
2493
2936
|
# improved to_f, works better where numerator and denominator are integers beyond the range of float, but their Quotient is still expressable as Float
|
2494
2937
|
def to_f
|
2495
|
-
num =
|
2496
|
-
den =
|
2938
|
+
num = numerator
|
2939
|
+
den = denominator
|
2497
2940
|
sign = num <=> 0
|
2498
2941
|
if (sign.zero?)
|
2499
2942
|
return 0.0
|
@@ -2579,9 +3022,7 @@ module LongMath
|
|
2579
3022
|
if (power.nil?)
|
2580
3023
|
power = powers_big_base ** n1
|
2581
3024
|
@@powers_of_big_base[n1] = power
|
2582
|
-
puts "npower_of_big_base(n1=#{n1}) c->#{power.size}"
|
2583
3025
|
else
|
2584
|
-
puts "npower_of_big_base(n1=#{n1}) c<-#{power.size}"
|
2585
3026
|
end
|
2586
3027
|
power
|
2587
3028
|
end
|
@@ -2736,6 +3177,60 @@ module LongMath
|
|
2736
3177
|
end
|
2737
3178
|
end
|
2738
3179
|
|
3180
|
+
LOG_1E100 = Math.log(1e100)
|
3181
|
+
|
3182
|
+
#
|
3183
|
+
# internal helper method for calculating the internal precision for power
|
3184
|
+
#
|
3185
|
+
def LongMath.calc_iprec_for_power(x, y, prec)
|
3186
|
+
|
3187
|
+
logx_f = LongMath.log_f(x.abs)
|
3188
|
+
x1 = (x.abs - 1).abs
|
3189
|
+
logx1_f = 0
|
3190
|
+
if (x1 > 0)
|
3191
|
+
logx1_f = LongMath.log_f(x1).abs
|
3192
|
+
end
|
3193
|
+
if (logx1_f > 5)
|
3194
|
+
prec = (prec * Math.log(logx1_f) + 5)
|
3195
|
+
end
|
3196
|
+
|
3197
|
+
y_f = nil
|
3198
|
+
if (y.abs <= LongMath::MAX_FLOATABLE) then
|
3199
|
+
y_f = y.to_f
|
3200
|
+
else
|
3201
|
+
y_f = y.round_to_scale(18, LongMath::ROUND_UP)
|
3202
|
+
end
|
3203
|
+
|
3204
|
+
logx_y_f = logx_f * y_f
|
3205
|
+
|
3206
|
+
if (logx_y_f.abs > LongMath::MAX_FLOATABLE) then
|
3207
|
+
raise ArgumentError, "power would be way too big: y*log(x)=#{logx_y_f}";
|
3208
|
+
end
|
3209
|
+
logx_y_f = logx_y_f.to_f unless logx_y_f.kind_of? Float
|
3210
|
+
|
3211
|
+
iprec_x = calc_iprec_for_exp(logx_y_f.abs.ceil, prec, logx_y_f < 0)
|
3212
|
+
iprec_y = iprec_x
|
3213
|
+
iprec = iprec_x + 2
|
3214
|
+
if (logx_f < 0)
|
3215
|
+
iprec_x -= (-1.5 + logx_f/LOG10).round
|
3216
|
+
end
|
3217
|
+
if (y_f.abs < 1)
|
3218
|
+
logy_f = LongMath.log_f(y.abs)
|
3219
|
+
iprec_y -= (- 1.5 + logy_f/LOG10).round
|
3220
|
+
end
|
3221
|
+
if (iprec > prec_limit)
|
3222
|
+
iprec = prec_limit
|
3223
|
+
end
|
3224
|
+
if (iprec_x > prec_limit)
|
3225
|
+
iprec_x = prec_limit
|
3226
|
+
end
|
3227
|
+
if (iprec_y > prec_limit)
|
3228
|
+
iprec_y = prec_limit
|
3229
|
+
end
|
3230
|
+
[ iprec, iprec_x, iprec_y, logx_y_f ]
|
3231
|
+
|
3232
|
+
end
|
3233
|
+
|
2739
3234
|
public
|
2740
3235
|
|
2741
3236
|
#
|
@@ -2749,11 +3244,8 @@ module LongMath
|
|
2749
3244
|
n1 = n >> POWERS_MED_EXP_PARAM
|
2750
3245
|
p = npower10_cached(n0)
|
2751
3246
|
if (n1 > 0) then
|
2752
|
-
puts "n0=#{n0} n1=#{n1}"
|
2753
3247
|
p1 = npower_of_big_base(n1)
|
2754
|
-
puts "n0=#{n0} n1=#{n1} p1"
|
2755
3248
|
p *= p1
|
2756
|
-
puts "n0=#{n0} n1=#{n1} p"
|
2757
3249
|
end
|
2758
3250
|
return p
|
2759
3251
|
end
|
@@ -2980,7 +3472,7 @@ module LongMath
|
|
2980
3472
|
check_is_int(x, "x")
|
2981
3473
|
check_is_int(n, "n")
|
2982
3474
|
|
2983
|
-
n2 = n<<1
|
3475
|
+
n2 = n << 1
|
2984
3476
|
n1 = n+1
|
2985
3477
|
check_word_len(n2, "2*n")
|
2986
3478
|
|
@@ -3281,6 +3773,38 @@ module LongMath
|
|
3281
3773
|
exp_internal(x, prec, mode)
|
3282
3774
|
end
|
3283
3775
|
|
3776
|
+
#
|
3777
|
+
# calc the base-2-exponential function of x to the given precision as
|
3778
|
+
# LongDecimal. Only supports values of x such that the result still
|
3779
|
+
# fits into a float (x <= 709). This limitation is somewhat
|
3780
|
+
# arbitrary, but it is enforced in order to avoid producing numbers
|
3781
|
+
# with the exponential function that exceed the memory.
|
3782
|
+
#
|
3783
|
+
# Warning: this is a transcendental function that aims at producing
|
3784
|
+
# results to the given precision, but minor inaccuracies of the
|
3785
|
+
# result have to be expected under some circumstances, being off by
|
3786
|
+
# one even more often.
|
3787
|
+
#
|
3788
|
+
def LongMath.exp2(x, prec, mode = LongMath.standard_mode) # down?
|
3789
|
+
LongMath.power(2, x, prec, mode)
|
3790
|
+
end
|
3791
|
+
|
3792
|
+
#
|
3793
|
+
# calc the base-10-exponential function of x to the given precision as
|
3794
|
+
# LongDecimal. Only supports values of x such that the result still
|
3795
|
+
# fits into a float (x <= 709). This limitation is somewhat
|
3796
|
+
# arbitrary, but it is enforced in order to avoid producing numbers
|
3797
|
+
# with the exponential function that exceed the memory.
|
3798
|
+
#
|
3799
|
+
# Warning: this is a transcendental function that aims at producing
|
3800
|
+
# results to the given precision, but minor inaccuracies of the
|
3801
|
+
# result have to be expected under some circumstances, being off by
|
3802
|
+
# one even more often.
|
3803
|
+
#
|
3804
|
+
def LongMath.exp10(x, prec, mode = LongMath.standard_mode) # down?
|
3805
|
+
LongMath.power(10, x, prec, mode)
|
3806
|
+
end
|
3807
|
+
|
3284
3808
|
private
|
3285
3809
|
|
3286
3810
|
#
|
@@ -3302,15 +3826,24 @@ module LongMath
|
|
3302
3826
|
iprec
|
3303
3827
|
end
|
3304
3828
|
|
3305
|
-
public
|
3306
|
-
|
3307
3829
|
#
|
3308
3830
|
# internal functionality of exp. exposes some more parameters, that
|
3309
|
-
# should usually be set to
|
3831
|
+
# should usually be set to default values, in order to allow better testing.
|
3310
3832
|
# do not actually call this method unless you are testing exp.
|
3311
3833
|
# create a bug report, if the default settings for the parameters do
|
3312
3834
|
# not work correctly
|
3313
3835
|
#
|
3836
|
+
# x is the number of which we want to calculate the exponential function.
|
3837
|
+
# prec is the precision that we need to achieve for the final result.
|
3838
|
+
# final_mode is the rounding mode that we use for the end result
|
3839
|
+
# j number of summands that are grouped together for better performance of the calculation
|
3840
|
+
# (defaults to the cube root of the desired number of base 2 digits to express the fractonal part of result.)
|
3841
|
+
# k is an integer such that calculation of exp(x/2**k) has good convergence. We calculate that and finally take a power of the result:
|
3842
|
+
# exp(x) = exp(x/2**k)**(2**k)
|
3843
|
+
# iprec is the precision used internally during the calculation
|
3844
|
+
# mode is the rounding mode used internally
|
3845
|
+
# cache_result indicates if the result of the calculation should be cached.
|
3846
|
+
#
|
3314
3847
|
def LongMath.exp_internal(x, prec = nil, final_mode = LongMath.standard_mode, j = nil, k = nil, iprec = nil, mode = LongMath.standard_imode, cache_result = true) # down?
|
3315
3848
|
|
3316
3849
|
if (prec.nil?) then
|
@@ -3330,16 +3863,20 @@ module LongMath
|
|
3330
3863
|
|
3331
3864
|
# if the result would come out to zero anyway, cut the work
|
3332
3865
|
xi = x.to_i
|
3333
|
-
# if (xi < -LongMath::MAX_FLOATABLE) || -((xi.to_f - 1) / LOG10) > prec+10 then
|
3334
3866
|
if (xi < -LongMath::MAX_FLOATABLE) || xi + 1 < -prec * LOG10 - LOG2 then
|
3867
|
+
# the result is 1/4 of the smallest positive number that can be expressed with the given precision.
|
3868
|
+
# this is rounded according to the final rounding mode, yielding 0.00000...00 or 0.00000...01.
|
3335
3869
|
return LongDecimal(25, prec+2).round_to_scale(prec, final_mode)
|
3336
3870
|
end
|
3871
|
+
|
3872
|
+
# if x is negative, do the calculation with -x and take 1/exp(-x) as the final result later.
|
3337
3873
|
x_was_neg = false
|
3338
3874
|
if (x < 0) then
|
3339
3875
|
x = -x
|
3340
3876
|
x_was_neg = true
|
3341
3877
|
end
|
3342
3878
|
|
3879
|
+
# calculate j and k if they are not given.
|
3343
3880
|
if j.nil? || k.nil? then
|
3344
3881
|
s1 = (prec * LOG10 / LOG2) ** (1.0/3.0)
|
3345
3882
|
if (j.nil?) then
|
@@ -3361,21 +3898,25 @@ module LongMath
|
|
3361
3898
|
check_is_int(j, "j")
|
3362
3899
|
check_is_int(k, "k")
|
3363
3900
|
|
3901
|
+
# calculate iprec if it is not given.
|
3364
3902
|
if (iprec.nil?) then
|
3365
3903
|
iprec = calc_iprec_for_exp(x, prec, x_was_neg)
|
3366
3904
|
end
|
3367
3905
|
iprec = check_is_prec(iprec, "iprec")
|
3368
3906
|
|
3369
|
-
# we only cache exp(1)
|
3907
|
+
# we only cache exp(1), exp(10), exp(100), exp(MAX_EXP_ABLE.to_i), otherwise cache_key is returned as nil.
|
3370
3908
|
cache_key = get_cache_key("exp", x, mode, [1, 10, 100, MAX_EXP_ABLE.to_i])
|
3371
3909
|
y_k = get_cached(cache_key, x, iprec)
|
3372
3910
|
|
3911
|
+
# if the cache did not yield our result, calculate it.
|
3373
3912
|
if (y_k.nil?) then
|
3374
3913
|
y_k = exp_raw(x, prec, j, k, iprec, mode)
|
3375
3914
|
|
3376
|
-
# keep result around for exp(1)
|
3915
|
+
# keep result around for exp(1), exp(10), exp(100), exp(MAX_EXP_ABLE.to_i)
|
3377
3916
|
set_cached(cache_key, y_k) if (cache_result)
|
3378
3917
|
end
|
3918
|
+
|
3919
|
+
# take 1/exp(-x) as result, if x was negative.
|
3379
3920
|
if (x_was_neg)
|
3380
3921
|
y_k = y_k.reciprocal
|
3381
3922
|
end
|
@@ -3388,25 +3929,48 @@ module LongMath
|
|
3388
3929
|
# calculation of exp(x) with precision used internally. Needs to be
|
3389
3930
|
# rounded to be accurate to all digits that are provided.
|
3390
3931
|
#
|
3932
|
+
# x is the number of which we want to calculate the exponential function.
|
3933
|
+
# prec is the precision that we need to achieve for the final result.
|
3934
|
+
# final_mode is the rounding mode that we use for the end result
|
3935
|
+
# j number of summands that are grouped together for better performance of the calculation
|
3936
|
+
# k is an integer such that calculation of exp(x/2**k) has good convergence. We calculate that and finally take a power of the result:
|
3937
|
+
# exp(x) = exp(x/2**k)**(2**k)
|
3938
|
+
# iprec is the precision used internally during the calculation
|
3939
|
+
# mode is the rounding mode used internally
|
3940
|
+
# cache_result indicates if the result of the calculation should be cached.
|
3941
|
+
#
|
3391
3942
|
def LongMath.exp_raw(x, prec, j, k, iprec, mode)
|
3392
3943
|
dprec = [ (iprec*0.9).round, prec ].max
|
3393
3944
|
|
3945
|
+
# convert to LongDecimal, if necessary:
|
3394
3946
|
unless (x.kind_of? LongDecimal)
|
3395
3947
|
x = x.to_ld(iprec, mode)
|
3396
3948
|
end
|
3397
3949
|
|
3950
|
+
# we use this x_k in the Taylor row:
|
3398
3951
|
x_k = (x / (1 << k)).round_to_scale(iprec, mode)
|
3952
|
+
# x_k ** j
|
3399
3953
|
x_j = (x_k ** j).round_to_scale(iprec, mode)
|
3954
|
+
# vector with j entries
|
3400
3955
|
s = [ LongDecimal(0) ] * j
|
3401
3956
|
t = LongDecimal(1)
|
3402
3957
|
last_t = 1
|
3403
3958
|
f = 0
|
3959
|
+
|
3960
|
+
# do the Taylor sum
|
3404
3961
|
loop do
|
3962
|
+
# do the partial loop: i=0..j-1
|
3963
|
+
# we avoid excessive multiplication with powers of x_k and keep these to the final calculation, thus having to apply them only once for each i.
|
3405
3964
|
j.times do |i|
|
3965
|
+
# s[i] = 1 / i! + x_k ** j / (i+j)! + x_k ** (2j) / (i+2*j)! + ... + x_k ** (n*j) / f!
|
3406
3966
|
s[i] += t
|
3407
3967
|
f += 1
|
3968
|
+
# adjust: t = x_k**(n*j)/f!
|
3408
3969
|
t = (t / f).round_to_scale(iprec, mode)
|
3970
|
+
break if (t.zero?)
|
3409
3971
|
end
|
3972
|
+
# multiply t by x_k**j. Detect when we can stop, if summands are zero or irrelevant for final result.
|
3973
|
+
break if (t.zero?)
|
3410
3974
|
t = (t * x_j).round_to_scale(iprec, mode)
|
3411
3975
|
break if (t.zero?)
|
3412
3976
|
tr = t.round_to_scale(dprec, LongDecimal::ROUND_DOWN).abs
|
@@ -3415,6 +3979,8 @@ module LongMath
|
|
3415
3979
|
break if (tr <= tu && last_t <= tu)
|
3416
3980
|
last_t = tr
|
3417
3981
|
end
|
3982
|
+
|
3983
|
+
# calculate result exp(x_k)
|
3418
3984
|
x_i = 1
|
3419
3985
|
y_k = LongDecimal(0)
|
3420
3986
|
j.times do |i|
|
@@ -3423,6 +3989,8 @@ module LongMath
|
|
3423
3989
|
end
|
3424
3990
|
y_k += (s[i] * x_i).round_to_scale(iprec, mode)
|
3425
3991
|
end
|
3992
|
+
|
3993
|
+
# square exp(x_k) k times to get exp(x):
|
3426
3994
|
k.times do |i|
|
3427
3995
|
y_k = y_k.square.round_to_scale(iprec, mode)
|
3428
3996
|
end
|
@@ -3430,6 +3998,8 @@ module LongMath
|
|
3430
3998
|
y_k
|
3431
3999
|
end # exp_raw
|
3432
4000
|
|
4001
|
+
public
|
4002
|
+
|
3433
4003
|
#
|
3434
4004
|
# calculate approximation of sqrt of a LongDecimal.
|
3435
4005
|
#
|
@@ -3476,7 +4046,7 @@ module LongMath
|
|
3476
4046
|
return y_arr
|
3477
4047
|
else
|
3478
4048
|
y, r = y_arr
|
3479
|
-
if ((mode == ROUND_HALF_EVEN || mode == ROUND_HALF_DOWN) && r > 0) then
|
4049
|
+
if ((mode == ROUND_HALF_EVEN || mode == ROUND_HALF_ODD || mode == ROUND_HALF_DOWN) && r > 0) then
|
3480
4050
|
mode = ROUND_HALF_UP
|
3481
4051
|
end
|
3482
4052
|
y = y.round_to_scale(prec, mode)
|
@@ -3544,7 +4114,7 @@ module LongMath
|
|
3544
4114
|
return y_arr
|
3545
4115
|
else
|
3546
4116
|
y, r = y_arr
|
3547
|
-
if ((mode == ROUND_HALF_EVEN || mode == ROUND_HALF_DOWN) && r > 0) then
|
4117
|
+
if ((mode == ROUND_HALF_EVEN || mode == ROUND_HALF_ODD || mode == ROUND_HALF_DOWN) && r > 0) then
|
3548
4118
|
mode = ROUND_HALF_UP
|
3549
4119
|
end
|
3550
4120
|
y = y.round_to_scale(prec, mode)
|
@@ -3699,6 +4269,904 @@ module LongMath
|
|
3699
4269
|
return y
|
3700
4270
|
end
|
3701
4271
|
|
4272
|
+
#
|
4273
|
+
# calculate the base 10 logarithm of x to the given precision as
|
4274
|
+
# LongDecimal.
|
4275
|
+
#
|
4276
|
+
# Warning: this is a transcendental function that aims at producing
|
4277
|
+
# results to the given precision, but minor inaccuracies of the
|
4278
|
+
# result have to be expected under some circumstances, being off by
|
4279
|
+
# one even more often.
|
4280
|
+
#
|
4281
|
+
def LongMath.log10(x, prec, mode = LongMath.standard_mode) # down?
|
4282
|
+
|
4283
|
+
prec = check_is_prec(prec, "prec")
|
4284
|
+
check_is_mode(mode, "mode")
|
4285
|
+
iprec = prec + 6
|
4286
|
+
unless (x.kind_of? LongDecimal)
|
4287
|
+
x = x.to_ld(iprec, mode)
|
4288
|
+
end
|
4289
|
+
if (x.one?) then
|
4290
|
+
return LongDecimal.zero!(prec)
|
4291
|
+
end
|
4292
|
+
|
4293
|
+
id = x.int_digits10
|
4294
|
+
xx = x.move_point_left(id)
|
4295
|
+
lnxx = log_internal(xx, iprec, mode)
|
4296
|
+
ln10 = log_internal(10, iprec, mode)
|
4297
|
+
y = id + (lnxx / ln10).round_to_scale(prec, mode)
|
4298
|
+
return y
|
4299
|
+
end
|
4300
|
+
|
4301
|
+
#
|
4302
|
+
# calculate the base 2 logarithm of x to the given precision as
|
4303
|
+
# LongDecimal.
|
4304
|
+
#
|
4305
|
+
# Warning: this is a transcendental function that aims at producing
|
4306
|
+
# results to the given precision, but minor inaccuracies of the
|
4307
|
+
# result have to be expected under some circumstances, being off by
|
4308
|
+
# one even more often.
|
4309
|
+
#
|
4310
|
+
def LongMath.log2(x, prec, mode = LongMath.standard_mode)
|
4311
|
+
|
4312
|
+
prec = check_is_prec(prec, "prec")
|
4313
|
+
check_is_mode(mode, "mode")
|
4314
|
+
iprec = prec + 6
|
4315
|
+
unless (x.kind_of? LongDecimal)
|
4316
|
+
x = x.to_ld(iprec, mode)
|
4317
|
+
end
|
4318
|
+
if (x.one?) then
|
4319
|
+
return LongDecimal.zero!(prec)
|
4320
|
+
end
|
4321
|
+
id = x.int_digits2
|
4322
|
+
xx = (x / (1 << id)).round_to_scale(x.scale+id)
|
4323
|
+
lnxx = log_internal(xx, iprec, mode)
|
4324
|
+
ln2 = log_internal(2.to_ld, iprec, mode)
|
4325
|
+
y = id + (lnxx / ln2).round_to_scale(prec, mode)
|
4326
|
+
return y
|
4327
|
+
end
|
4328
|
+
|
4329
|
+
#
|
4330
|
+
# calculate the natural logarithm of x as floating point number,
|
4331
|
+
# even if x cannot reasonably be expressed as Float.
|
4332
|
+
#
|
4333
|
+
# Warning: this is a transcendental function that aims at producing
|
4334
|
+
# results to the given precision, but minor inaccuracies of the
|
4335
|
+
# result have to be expected under some circumstances, being off by
|
4336
|
+
# one even more often.
|
4337
|
+
#
|
4338
|
+
def LongMath.log_f(x)
|
4339
|
+
raise TypeError, "x=#{x.inspect} must not be positive" unless x > 0
|
4340
|
+
unless x.kind_of? LongDecimal
|
4341
|
+
x_rounded = x.to_ld(18, LongDecimalRoundingMode::ROUND_HALF_UP)
|
4342
|
+
if (x_rounded.one?)
|
4343
|
+
# x rounds to 1, if we cut of the last digits?
|
4344
|
+
# near 1 the derivative of log(x) is approximately 1, so we can assume log_f(x) ~ x-1
|
4345
|
+
return x - 1
|
4346
|
+
else
|
4347
|
+
x = x_rounded
|
4348
|
+
end
|
4349
|
+
end
|
4350
|
+
y = 0
|
4351
|
+
while (x > LongMath::MAX_FLOATABLE)
|
4352
|
+
y += LOG_1E100
|
4353
|
+
x = x.move_point_left(100)
|
4354
|
+
end
|
4355
|
+
while (x < LongMath::MIN_FLOATABLE)
|
4356
|
+
y -= LOG_1E100
|
4357
|
+
x = x.move_point_right(100)
|
4358
|
+
end
|
4359
|
+
x_f = x.to_f
|
4360
|
+
y += Math.log(x_f)
|
4361
|
+
y
|
4362
|
+
end
|
4363
|
+
|
4364
|
+
# logarithms of integers to base 2
|
4365
|
+
LOGARR = [ nil, \
|
4366
|
+
0.0, \
|
4367
|
+
1.0, \
|
4368
|
+
1.58496250072116, \
|
4369
|
+
2.0, \
|
4370
|
+
2.32192809488736, \
|
4371
|
+
2.58496250072116, \
|
4372
|
+
2.8073549220576, \
|
4373
|
+
3.0, \
|
4374
|
+
3.16992500144231, \
|
4375
|
+
3.32192809488736, \
|
4376
|
+
3.4594316186373, \
|
4377
|
+
3.58496250072116, \
|
4378
|
+
3.70043971814109, \
|
4379
|
+
3.8073549220576, \
|
4380
|
+
3.90689059560852, \
|
4381
|
+
4.0, \
|
4382
|
+
4.08746284125034, \
|
4383
|
+
4.16992500144231, \
|
4384
|
+
4.24792751344359, \
|
4385
|
+
4.32192809488736, \
|
4386
|
+
4.39231742277876, \
|
4387
|
+
4.4594316186373, \
|
4388
|
+
4.52356195605701, \
|
4389
|
+
4.58496250072116, \
|
4390
|
+
4.64385618977472, \
|
4391
|
+
4.70043971814109, \
|
4392
|
+
4.75488750216347, \
|
4393
|
+
4.8073549220576, \
|
4394
|
+
4.85798099512757, \
|
4395
|
+
4.90689059560852, \
|
4396
|
+
4.95419631038688, \
|
4397
|
+
5.0, \
|
4398
|
+
5.04439411935845, \
|
4399
|
+
5.08746284125034, \
|
4400
|
+
5.12928301694497, \
|
4401
|
+
5.16992500144231, \
|
4402
|
+
5.20945336562895, \
|
4403
|
+
5.24792751344359, \
|
4404
|
+
5.28540221886225, \
|
4405
|
+
5.32192809488736, \
|
4406
|
+
5.35755200461808, \
|
4407
|
+
5.39231742277876, \
|
4408
|
+
5.4262647547021, \
|
4409
|
+
5.4594316186373, \
|
4410
|
+
5.49185309632967, \
|
4411
|
+
5.52356195605701, \
|
4412
|
+
5.55458885167764, \
|
4413
|
+
5.58496250072116, \
|
4414
|
+
5.61470984411521, \
|
4415
|
+
5.64385618977472, \
|
4416
|
+
5.6724253419715, \
|
4417
|
+
5.70043971814109, \
|
4418
|
+
5.7279204545632, \
|
4419
|
+
5.75488750216347, \
|
4420
|
+
5.78135971352466, \
|
4421
|
+
5.8073549220576, \
|
4422
|
+
5.83289001416474, \
|
4423
|
+
5.85798099512757, \
|
4424
|
+
5.88264304936184, \
|
4425
|
+
5.90689059560852, \
|
4426
|
+
5.93073733756289, \
|
4427
|
+
5.95419631038688, \
|
4428
|
+
5.97727992349992, \
|
4429
|
+
6.0, \
|
4430
|
+
6.02236781302845, \
|
4431
|
+
6.04439411935845, \
|
4432
|
+
6.06608919045777, \
|
4433
|
+
6.08746284125034, \
|
4434
|
+
6.10852445677817, \
|
4435
|
+
6.12928301694497, \
|
4436
|
+
6.14974711950468, \
|
4437
|
+
6.16992500144231, \
|
4438
|
+
6.18982455888002, \
|
4439
|
+
6.20945336562895, \
|
4440
|
+
6.22881869049588, \
|
4441
|
+
6.24792751344359, \
|
4442
|
+
6.2667865406949, \
|
4443
|
+
6.28540221886225, \
|
4444
|
+
6.3037807481771, \
|
4445
|
+
6.32192809488736, \
|
4446
|
+
6.33985000288463, \
|
4447
|
+
6.35755200461808, \
|
4448
|
+
6.37503943134693, \
|
4449
|
+
6.39231742277876, \
|
4450
|
+
6.4093909361377, \
|
4451
|
+
6.4262647547021, \
|
4452
|
+
6.44294349584873, \
|
4453
|
+
6.4594316186373, \
|
4454
|
+
6.4757334309664, \
|
4455
|
+
6.49185309632967, \
|
4456
|
+
6.5077946401987, \
|
4457
|
+
6.52356195605701, \
|
4458
|
+
6.53915881110803, \
|
4459
|
+
6.55458885167764, \
|
4460
|
+
6.56985560833095, \
|
4461
|
+
6.58496250072116, \
|
4462
|
+
6.59991284218713, \
|
4463
|
+
6.61470984411521, \
|
4464
|
+
6.62935662007961, \
|
4465
|
+
6.64385618977473, \
|
4466
|
+
6.6582114827518, \
|
4467
|
+
6.6724253419715, \
|
4468
|
+
6.68650052718322, \
|
4469
|
+
6.70043971814109, \
|
4470
|
+
6.71424551766612, \
|
4471
|
+
6.7279204545632, \
|
4472
|
+
6.74146698640115, \
|
4473
|
+
6.75488750216347, \
|
4474
|
+
6.76818432477693, \
|
4475
|
+
6.78135971352466, \
|
4476
|
+
6.79441586635011, \
|
4477
|
+
6.8073549220576, \
|
4478
|
+
6.82017896241519, \
|
4479
|
+
6.83289001416474, \
|
4480
|
+
6.84549005094438, \
|
4481
|
+
6.85798099512757, \
|
4482
|
+
6.8703647195834, \
|
4483
|
+
6.88264304936184, \
|
4484
|
+
6.89481776330794, \
|
4485
|
+
6.90689059560852, \
|
4486
|
+
6.9188632372746, \
|
4487
|
+
6.93073733756289, \
|
4488
|
+
6.94251450533924, \
|
4489
|
+
6.95419631038688, \
|
4490
|
+
6.96578428466209, \
|
4491
|
+
6.97727992349992, \
|
4492
|
+
6.98868468677217, \
|
4493
|
+
7.0, \
|
4494
|
+
7.01122725542325, \
|
4495
|
+
7.02236781302845, \
|
4496
|
+
7.03342300153745, \
|
4497
|
+
7.04439411935845, \
|
4498
|
+
7.05528243550119, \
|
4499
|
+
7.06608919045777, \
|
4500
|
+
7.07681559705083, \
|
4501
|
+
7.08746284125034, \
|
4502
|
+
7.09803208296053, \
|
4503
|
+
7.10852445677817, \
|
4504
|
+
7.11894107272351, \
|
4505
|
+
7.12928301694497, \
|
4506
|
+
7.13955135239879, \
|
4507
|
+
7.14974711950468, \
|
4508
|
+
7.15987133677839, \
|
4509
|
+
7.16992500144231, \
|
4510
|
+
7.17990909001493, \
|
4511
|
+
7.18982455888002, \
|
4512
|
+
7.19967234483636, \
|
4513
|
+
7.20945336562895, \
|
4514
|
+
7.21916852046216, \
|
4515
|
+
7.22881869049588, \
|
4516
|
+
7.23840473932508, \
|
4517
|
+
7.24792751344359, \
|
4518
|
+
7.25738784269265, \
|
4519
|
+
7.2667865406949, \
|
4520
|
+
7.27612440527424, \
|
4521
|
+
7.28540221886225, \
|
4522
|
+
7.29462074889163, \
|
4523
|
+
7.3037807481771, \
|
4524
|
+
7.31288295528436, \
|
4525
|
+
7.32192809488736, \
|
4526
|
+
7.33091687811462, \
|
4527
|
+
7.33985000288462, \
|
4528
|
+
7.34872815423108, \
|
4529
|
+
7.35755200461808, \
|
4530
|
+
7.36632221424582, \
|
4531
|
+
7.37503943134693, \
|
4532
|
+
7.38370429247405, \
|
4533
|
+
7.39231742277876, \
|
4534
|
+
7.40087943628218, \
|
4535
|
+
7.4093909361377, \
|
4536
|
+
7.4178525148859, \
|
4537
|
+
7.4262647547021, \
|
4538
|
+
7.43462822763673, \
|
4539
|
+
7.44294349584873, \
|
4540
|
+
7.45121111183233, \
|
4541
|
+
7.4594316186373, \
|
4542
|
+
7.467605550083, \
|
4543
|
+
7.4757334309664, \
|
4544
|
+
7.48381577726426, \
|
4545
|
+
7.49185309632967, \
|
4546
|
+
7.49984588708321, \
|
4547
|
+
7.5077946401987, \
|
4548
|
+
7.51569983828404, \
|
4549
|
+
7.52356195605701, \
|
4550
|
+
7.53138146051631, \
|
4551
|
+
7.53915881110803, \
|
4552
|
+
7.54689445988764, \
|
4553
|
+
7.55458885167764, \
|
4554
|
+
7.56224242422107, \
|
4555
|
+
7.56985560833095, \
|
4556
|
+
7.57742882803575, \
|
4557
|
+
7.58496250072116, \
|
4558
|
+
7.59245703726808, \
|
4559
|
+
7.59991284218713, \
|
4560
|
+
7.60733031374961, \
|
4561
|
+
7.61470984411521, \
|
4562
|
+
7.62205181945638, \
|
4563
|
+
7.62935662007961, \
|
4564
|
+
7.63662462054365, \
|
4565
|
+
7.64385618977472, \
|
4566
|
+
7.65105169117893, \
|
4567
|
+
7.6582114827518, \
|
4568
|
+
7.66533591718518, \
|
4569
|
+
7.6724253419715, \
|
4570
|
+
7.67948009950545, \
|
4571
|
+
7.68650052718322, \
|
4572
|
+
7.69348695749933, \
|
4573
|
+
7.70043971814109, \
|
4574
|
+
7.70735913208088, \
|
4575
|
+
7.71424551766612, \
|
4576
|
+
7.72109918870719, \
|
4577
|
+
7.7279204545632, \
|
4578
|
+
7.73470962022584, \
|
4579
|
+
7.74146698640115, \
|
4580
|
+
7.74819284958946, \
|
4581
|
+
7.75488750216347, \
|
4582
|
+
7.76155123244448, \
|
4583
|
+
7.76818432477693, \
|
4584
|
+
7.77478705960117, \
|
4585
|
+
7.78135971352466, \
|
4586
|
+
7.78790255939143, \
|
4587
|
+
7.79441586635011, \
|
4588
|
+
7.8008998999203, \
|
4589
|
+
7.8073549220576, \
|
4590
|
+
7.81378119121704, \
|
4591
|
+
7.82017896241519, \
|
4592
|
+
7.82654848729092, \
|
4593
|
+
7.83289001416474, \
|
4594
|
+
7.83920378809694, \
|
4595
|
+
7.84549005094438, \
|
4596
|
+
7.85174904141606, \
|
4597
|
+
7.85798099512757, \
|
4598
|
+
7.86418614465428, \
|
4599
|
+
7.8703647195834, \
|
4600
|
+
7.876516946565, \
|
4601
|
+
7.88264304936184, \
|
4602
|
+
7.88874324889826, \
|
4603
|
+
7.89481776330794, \
|
4604
|
+
7.90086680798075, \
|
4605
|
+
7.90689059560852, \
|
4606
|
+
7.91288933622996, \
|
4607
|
+
7.9188632372746, \
|
4608
|
+
7.92481250360578, \
|
4609
|
+
7.93073733756289, \
|
4610
|
+
7.93663793900257, \
|
4611
|
+
7.94251450533924, \
|
4612
|
+
7.94836723158468, \
|
4613
|
+
7.95419631038688, \
|
4614
|
+
7.96000193206808, \
|
4615
|
+
7.96578428466209, \
|
4616
|
+
7.97154355395077, \
|
4617
|
+
7.97727992349992, \
|
4618
|
+
7.98299357469431, \
|
4619
|
+
7.98868468677217, \
|
4620
|
+
7.99435343685886, \
|
4621
|
+
8.0 ]
|
4622
|
+
|
4623
|
+
def LongMath.log2int(x)
|
4624
|
+
unless x.kind_of? Integer
|
4625
|
+
raise TypeError, "x=#{x.inspect} must be Integer"
|
4626
|
+
end
|
4627
|
+
if (x <= 0)
|
4628
|
+
raise ArgumentError, "x=#{x} < 0"
|
4629
|
+
end
|
4630
|
+
|
4631
|
+
s = x.size
|
4632
|
+
l = [ 8 * s - 36 , 0 ].max
|
4633
|
+
|
4634
|
+
xx = x >> l
|
4635
|
+
while xx >= 256
|
4636
|
+
l += 1
|
4637
|
+
xx = xx >> 1
|
4638
|
+
end
|
4639
|
+
yy = LOGARR[xx]
|
4640
|
+
y = l + yy
|
4641
|
+
y
|
4642
|
+
end
|
4643
|
+
|
4644
|
+
#
|
4645
|
+
# calc the power of x with exponent y to the given precision as
|
4646
|
+
# LongDecimal. Only supports values of y such that the result still
|
4647
|
+
# fits into a float
|
4648
|
+
#
|
4649
|
+
# Warning: this is a transcendental function that aims at producing
|
4650
|
+
# results to the given precision, but minor inaccuracies of the
|
4651
|
+
# result have to be expected under some circumstances, being off by
|
4652
|
+
# one even more often.
|
4653
|
+
#
|
4654
|
+
def LongMath.power(x, y, prec, mode = LongMath.standard_mode)
|
4655
|
+
|
4656
|
+
raise TypeError, "x=#{x} must be numeric" unless x.kind_of? Numeric
|
4657
|
+
raise TypeError, "y=#{y} must be numeric" unless y.kind_of? Numeric
|
4658
|
+
raise TypeError, "x=#{x.inspect} must not be greater #{MAX_FLOATABLE}" unless x.abs <= MAX_FLOATABLE
|
4659
|
+
raise TypeError, "y=#{y.inspect} must not be greater #{MAX_FLOATABLE}" unless y.abs <= MAX_FLOATABLE
|
4660
|
+
raise TypeError, "y=#{y.inspect} must not be negative if base is zero}" if y < 0 && x.zero?
|
4661
|
+
raise TypeError, "x=#{x.inspect} must not negative" unless x >= 0
|
4662
|
+
prec = check_is_prec(prec, "prec")
|
4663
|
+
check_is_mode(mode, "mode")
|
4664
|
+
|
4665
|
+
# handle the special cases where base or exponent are 0 or 1 explicitely
|
4666
|
+
if y.zero? then
|
4667
|
+
return LongDecimal.one!(prec)
|
4668
|
+
elsif x.zero? then
|
4669
|
+
return LongDecimal.zero!(prec)
|
4670
|
+
elsif y.one? then
|
4671
|
+
return x.to_ld(prec, mode)
|
4672
|
+
elsif x.one? then
|
4673
|
+
return LongDecimal.one!(prec)
|
4674
|
+
end
|
4675
|
+
|
4676
|
+
# could be result with our precision
|
4677
|
+
# x ** y <= 10**-s/2 <=> y * log(x) <= -s log(10) - log(2)
|
4678
|
+
|
4679
|
+
iprec, iprec_x, iprec_y, logx_y_f = calc_iprec_for_power(x, y, prec)
|
4680
|
+
$stdout.flush
|
4681
|
+
if (x < 1 && y > 0 || x > 1 && y < 0) then
|
4682
|
+
if (logx_y_f <= - prec * LOG10 - LOG2) then
|
4683
|
+
return LongDecimal.zero!(prec)
|
4684
|
+
end
|
4685
|
+
end
|
4686
|
+
|
4687
|
+
unless (x.kind_of? LongDecimalBase) || (x.kind_of? Integer)
|
4688
|
+
x = x.to_ld(iprec_x, mode)
|
4689
|
+
end
|
4690
|
+
unless (y.kind_of? LongDecimalBase) || (y.kind_of? Integer)
|
4691
|
+
y = y.to_ld(iprec_y, mode)
|
4692
|
+
end
|
4693
|
+
|
4694
|
+
# try shortcut if exponent is an integer
|
4695
|
+
if (y.kind_of? LongDecimalBase) && y.is_int? then
|
4696
|
+
y = y.to_i
|
4697
|
+
end
|
4698
|
+
unless (y.kind_of? Integer)
|
4699
|
+
y2 = y*2
|
4700
|
+
if (y2.kind_of? LongDecimalBase) && y2.is_int? then
|
4701
|
+
y2 = y2.to_i
|
4702
|
+
end
|
4703
|
+
if (y2.kind_of? Integer)
|
4704
|
+
x = LongMath.sqrt(x, 2*iprec_x, mode)
|
4705
|
+
y = y2
|
4706
|
+
end
|
4707
|
+
end
|
4708
|
+
if (y.kind_of? Integer)
|
4709
|
+
unless x.kind_of? LongDecimal
|
4710
|
+
x = x.to_ld(iprec_x)
|
4711
|
+
end
|
4712
|
+
z = LongMath.ipower(x, y, 2*iprec, mode)
|
4713
|
+
return z.to_ld(prec, mode)
|
4714
|
+
end
|
4715
|
+
|
4716
|
+
# it can be assumed that the exponent is not an integer, so it should
|
4717
|
+
# be converted into LongDecimal
|
4718
|
+
unless (y.kind_of? LongDecimal)
|
4719
|
+
y = y.to_ld(iprec_y, mode)
|
4720
|
+
end
|
4721
|
+
|
4722
|
+
# if x < 1 && y < 0 then
|
4723
|
+
# working with x < 1 should be improved, less precision needed
|
4724
|
+
if x < 1 then
|
4725
|
+
# since we do not allow x < 0 and we have handled x = 0 already,
|
4726
|
+
# we can be sure that x is no integer, so it has been converted
|
4727
|
+
# if necessary to LongDecimalBase
|
4728
|
+
y = -y
|
4729
|
+
x = (1/x).round_to_scale(iprec_x*2, mode)
|
4730
|
+
iprec, iprec_x, iprec_y, logx_y_f = calc_iprec_for_power(x, y, prec)
|
4731
|
+
$stdout.flush
|
4732
|
+
if (x < 1 && y > 0 || x > 1 && y < 0) then
|
4733
|
+
if (logx_y_f <= - prec * LOG10 - LOG2) then
|
4734
|
+
return LongDecimal.zero!(prec)
|
4735
|
+
end
|
4736
|
+
end
|
4737
|
+
end
|
4738
|
+
|
4739
|
+
# exponent is split in two parts, an integer part and a
|
4740
|
+
# LongDecimal with absolute value <= 0.5
|
4741
|
+
y0 = y.round_to_scale(0, LongMath.standard_imode).to_i
|
4742
|
+
x0 = x
|
4743
|
+
point_shift = 0
|
4744
|
+
while x0 > LongMath::MAX_FLOATABLE
|
4745
|
+
x0 = x0.move_point_left(100)
|
4746
|
+
point_shift += 100
|
4747
|
+
end
|
4748
|
+
iprec2 = 2 * (iprec + point_shift)
|
4749
|
+
iprec3 = [ iprec2, LongMath.prec_limit() - 24 ].min
|
4750
|
+
z0 = LongMath.ipower(x0, y0, iprec3, mode)
|
4751
|
+
if (point_shift > 0)
|
4752
|
+
unless z0.kind_of? LongDecimal
|
4753
|
+
z0 = z0.to_ld(2*(iprec + point_shift))
|
4754
|
+
end
|
4755
|
+
z0 = z0.move_point_right(point_shift * y0)
|
4756
|
+
end
|
4757
|
+
y1 = y - y0
|
4758
|
+
prec_extra = 0
|
4759
|
+
if (y0 > 0)
|
4760
|
+
prec_extra = (y0*Math.log10(x.to_f).abs).ceil
|
4761
|
+
end
|
4762
|
+
# z1 = LongMath.power_internal(x, y1, prec+prec_extra , mode)
|
4763
|
+
z1 = LongMath.power_internal(x, y1, prec+prec_extra + 4, mode)
|
4764
|
+
z = z0 * z1
|
4765
|
+
return z.to_ld(prec, mode)
|
4766
|
+
end
|
4767
|
+
|
4768
|
+
#
|
4769
|
+
# internal functionality to calculate the y-th power of x assuming
|
4770
|
+
# that y is an integer
|
4771
|
+
# prec is a hint on how much internal precision is needed at most
|
4772
|
+
# final rounding is left to the caller
|
4773
|
+
#
|
4774
|
+
def LongMath.ipower(x, y, prec, mode)
|
4775
|
+
|
4776
|
+
t0 = Time.now
|
4777
|
+
raise TypeError, "base x=#{x} must be numeric" unless x.kind_of? Numeric
|
4778
|
+
raise TypeError, "exponent y=#{y} must be integer" unless y.kind_of? Integer
|
4779
|
+
raise TypeError, "base x=#{x.inspect} must not be greater MAX_FLOATABLE=#{MAX_FLOATABLE}" unless x.abs <= MAX_FLOATABLE
|
4780
|
+
raise TypeError, "exponent y=#{y.inspect} must not be greater MAX_FLOATABLE=#{MAX_FLOATABLE}" unless y.abs <= MAX_FLOATABLE
|
4781
|
+
prec = check_is_prec(prec, "prec")
|
4782
|
+
check_is_mode(mode, "mode")
|
4783
|
+
|
4784
|
+
if (y.zero?)
|
4785
|
+
return 1
|
4786
|
+
elsif ! (x.kind_of? LongDecimalBase) || x.scale * y.abs <= prec
|
4787
|
+
return x ** y
|
4788
|
+
elsif (y < 0)
|
4789
|
+
l = Math.log10(x.abs.to_f)
|
4790
|
+
if (l > 0)
|
4791
|
+
prec += (2*l).ceil
|
4792
|
+
end
|
4793
|
+
# return (1/LongMath.ipower(x, -y, prec + 2, mode)).round_to_scale(prec, mode)
|
4794
|
+
xi = 1/x
|
4795
|
+
xr = xi.round_to_scale(prec + 6, mode)
|
4796
|
+
return LongMath.ipower(xr, -y, prec, mode)
|
4797
|
+
else
|
4798
|
+
# y > 0
|
4799
|
+
cnt = 0
|
4800
|
+
z = x
|
4801
|
+
y0 = y
|
4802
|
+
x0 = x
|
4803
|
+
while true do
|
4804
|
+
|
4805
|
+
cnt++
|
4806
|
+
y -= 1
|
4807
|
+
if (y.zero?)
|
4808
|
+
break
|
4809
|
+
end
|
4810
|
+
while (y & 0x01) == 0 do
|
4811
|
+
|
4812
|
+
cnt++
|
4813
|
+
y = y >> 1
|
4814
|
+
x = (x*x)
|
4815
|
+
if (x.kind_of? LongDecimalBase)
|
4816
|
+
x = x.round_to_scale(prec+4, mode)
|
4817
|
+
end
|
4818
|
+
if (cnt > 1000)
|
4819
|
+
cnt = 0
|
4820
|
+
end
|
4821
|
+
|
4822
|
+
end
|
4823
|
+
z *= x
|
4824
|
+
if (z.kind_of? LongDecimalBase)
|
4825
|
+
z = z.round_to_scale(prec+3, mode)
|
4826
|
+
if (z.zero?)
|
4827
|
+
break
|
4828
|
+
end
|
4829
|
+
end
|
4830
|
+
end
|
4831
|
+
z = z.round_to_scale(prec, mode)
|
4832
|
+
return z
|
4833
|
+
end
|
4834
|
+
end
|
4835
|
+
|
4836
|
+
#
|
4837
|
+
# internal functionality of exp. exposes some more parameters, that
|
4838
|
+
# should usually be set to defaut values, in order to allow better testing.
|
4839
|
+
# do not actually call this method unless you are testing exp.
|
4840
|
+
# create a bug report, if the default settings for the parameters do
|
4841
|
+
# not work correctly
|
4842
|
+
#
|
4843
|
+
def LongMath.power_internal(x, y, prec = nil, final_mode = LongMath.standard_mode, iprec = nil, mode = LongMath.standard_imode)
|
4844
|
+
|
4845
|
+
if (prec.nil?) then
|
4846
|
+
if (x.kind_of? LongDecimalBase) && (y.kind_of? LongDecimalBase)
|
4847
|
+
prec = [x.scale, y.scale].max
|
4848
|
+
elsif (x.kind_of? LongDecimalBase)
|
4849
|
+
prec = x.scale
|
4850
|
+
elsif (y.kind_of? LongDecimalBase)
|
4851
|
+
prec = y.scale
|
4852
|
+
else
|
4853
|
+
raise ArgumentError, "precision must be supplied either as precision of x=#{x} or explicitely"
|
4854
|
+
end
|
4855
|
+
end
|
4856
|
+
prec = check_is_prec(prec, "prec")
|
4857
|
+
|
4858
|
+
if (final_mode.nil?)
|
4859
|
+
final_mode = LongMath.standard_mode
|
4860
|
+
end
|
4861
|
+
check_is_mode(final_mode, "final_mode")
|
4862
|
+
check_is_mode(mode, "mode")
|
4863
|
+
|
4864
|
+
if y.zero? then
|
4865
|
+
return LongDecimal.one!(prec)
|
4866
|
+
elsif x.zero? then
|
4867
|
+
return LongDecimal.zero!(prec)
|
4868
|
+
end
|
4869
|
+
|
4870
|
+
if (iprec.nil?) then
|
4871
|
+
iprec, iprec_x, iprec_y = calc_iprec_for_power(x, y, prec)
|
4872
|
+
end
|
4873
|
+
unless (x.kind_of? LongDecimal)
|
4874
|
+
# x = x.to_ld(iprec, mode)
|
4875
|
+
x = x.to_ld(iprec_x, mode)
|
4876
|
+
end
|
4877
|
+
unless (y.kind_of? LongDecimal)
|
4878
|
+
# y = y.to_ld(iprec, mode)
|
4879
|
+
y = y.to_ld(iprec_y, mode)
|
4880
|
+
end
|
4881
|
+
|
4882
|
+
# logx = log(x, iprec, mode)
|
4883
|
+
logx = log(x, iprec + 20, mode)
|
4884
|
+
logx_y = logx*y
|
4885
|
+
# xy = exp_internal(logx_y, prec + 1, mode)
|
4886
|
+
# xy = exp_internal(logx_y, prec + 4, mode)
|
4887
|
+
xy = exp_internal(logx_y, prec + 3, mode)
|
4888
|
+
xy.round_to_scale(prec, final_mode)
|
4889
|
+
|
4890
|
+
end # power_internal
|
4891
|
+
|
4892
|
+
# helper for means
|
4893
|
+
def LongMath.sign_check_for_mean(allow_different_signs, *args)
|
4894
|
+
if (args.empty?)
|
4895
|
+
raise ArgumentError, "cannot calculate mean of empty array"
|
4896
|
+
end
|
4897
|
+
first = args[0]
|
4898
|
+
has_neg = false
|
4899
|
+
has_pos = false
|
4900
|
+
has_zero = false
|
4901
|
+
all_same = true
|
4902
|
+
result_sign = 0
|
4903
|
+
args.each do |x|
|
4904
|
+
if (! (x.kind_of? Numeric))
|
4905
|
+
raise ArgumentError, "cannot calculate mean of a non-numeric array #{args.inspect}"
|
4906
|
+
end
|
4907
|
+
if (x.kind_of? Complex)
|
4908
|
+
raise ArgumentError, "mean not supported for complex numbers args=#{args.inspect}"
|
4909
|
+
end
|
4910
|
+
if (all_same && x != first)
|
4911
|
+
all_same = false
|
4912
|
+
end
|
4913
|
+
sign = x.sgn
|
4914
|
+
if (sign < 0)
|
4915
|
+
has_neg = true
|
4916
|
+
result_sign = sign
|
4917
|
+
elsif (sign > 0)
|
4918
|
+
has_pos = true
|
4919
|
+
result_sign = sign
|
4920
|
+
else
|
4921
|
+
has_zero = true
|
4922
|
+
end
|
4923
|
+
if (has_neg && has_pos && !allow_different_signs)
|
4924
|
+
raise ArgumentError, "signs of parameters have to match for quadratic mean args=#{args.inspect}"
|
4925
|
+
end
|
4926
|
+
end
|
4927
|
+
return result_sign, all_same, has_neg, has_zero, has_pos
|
4928
|
+
end
|
4929
|
+
|
4930
|
+
public
|
4931
|
+
|
4932
|
+
# arithmetic mean
|
4933
|
+
def LongMath.arithmetic_mean(new_scale, rounding_mode, *args)
|
4934
|
+
if (args.empty?)
|
4935
|
+
raise ArgumentError, "cannot calculate average of empty array"
|
4936
|
+
end
|
4937
|
+
sum = args.inject(0) do |psum,x|
|
4938
|
+
psum + x
|
4939
|
+
end
|
4940
|
+
raw_result = if (sum.kind_of? Integer)
|
4941
|
+
Rational(sum, args.size)
|
4942
|
+
else
|
4943
|
+
sum / args.size
|
4944
|
+
end
|
4945
|
+
result = raw_result.to_ld(new_scale, rounding_mode)
|
4946
|
+
return result
|
4947
|
+
end
|
4948
|
+
|
4949
|
+
class << LongMath
|
4950
|
+
alias_method :average, :arithmetic_mean
|
4951
|
+
end
|
4952
|
+
|
4953
|
+
# geometric mean
|
4954
|
+
def LongMath.geometric_mean(new_scale, rounding_mode, *args)
|
4955
|
+
result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(true, *args)
|
4956
|
+
if (all_same)
|
4957
|
+
return args[0].to_ld(new_scale, rounding_mode)
|
4958
|
+
end
|
4959
|
+
if (has_zero)
|
4960
|
+
return LongDecimal.zero!(new_scale)
|
4961
|
+
end
|
4962
|
+
prod = args.inject(1) do |pprod,x|
|
4963
|
+
pprod * x.abs
|
4964
|
+
end
|
4965
|
+
result = nil
|
4966
|
+
n = args.size
|
4967
|
+
if (n == 0 || n == 1)
|
4968
|
+
result = prod.to_ld(new_scale, rounding_mode)
|
4969
|
+
elsif (n == 2)
|
4970
|
+
result = LongMath.sqrt(prod, new_scale, rounding_mode)
|
4971
|
+
elsif (n == 3)
|
4972
|
+
result = LongMath.cbrt(prod, new_scale, rounding_mode)
|
4973
|
+
else
|
4974
|
+
result = LongMath.power(prod, Rational(1, args.size), new_scale, rounding_mode)
|
4975
|
+
end
|
4976
|
+
if (has_neg)
|
4977
|
+
result = -result
|
4978
|
+
end
|
4979
|
+
return result
|
4980
|
+
end
|
4981
|
+
|
4982
|
+
# harmonic mean
|
4983
|
+
def LongMath.harmonic_mean(new_scale, rounding_mode, *args)
|
4984
|
+
result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(true, *args)
|
4985
|
+
if (all_same)
|
4986
|
+
return args[0].to_ld(new_scale, rounding_mode)
|
4987
|
+
end
|
4988
|
+
if (has_zero)
|
4989
|
+
raise ArgumentError, "cannot calculate harmonic mean of argument list containing zero #{args.inspect}"
|
4990
|
+
end
|
4991
|
+
sum = args.inject(0) do |psum, x|
|
4992
|
+
psum + if (x.kind_of? Integer)
|
4993
|
+
Rational(1, x)
|
4994
|
+
else
|
4995
|
+
1/x
|
4996
|
+
end
|
4997
|
+
end
|
4998
|
+
raw_result = args.size / sum
|
4999
|
+
result = raw_result.to_ld(new_scale, rounding_mode)
|
5000
|
+
return result
|
5001
|
+
end
|
5002
|
+
|
5003
|
+
# arithmetic-geometric mean (AGM)
|
5004
|
+
def LongMath.arithmetic_geometric_mean(new_scale, rounding_mode, *args)
|
5005
|
+
result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(true, *args)
|
5006
|
+
if (all_same)
|
5007
|
+
return args[0].to_ld(new_scale, rounding_mode)
|
5008
|
+
end
|
5009
|
+
prec = (new_scale * 1.1 + 20).to_i
|
5010
|
+
x = arithmetic_mean(prec, rounding_mode, *args)
|
5011
|
+
y = geometric_mean(prec, rounding_mode, *args)
|
5012
|
+
delta = 3*x.unit
|
5013
|
+
while ((x-y).abs >= delta)
|
5014
|
+
xn = arithmetic_mean(prec, rounding_mode, x, y)
|
5015
|
+
yn = geometric_mean(prec, rounding_mode, x, y)
|
5016
|
+
x = xn
|
5017
|
+
y = yn
|
5018
|
+
end
|
5019
|
+
return x.round_to_scale(new_scale, rounding_mode)
|
5020
|
+
end
|
5021
|
+
|
5022
|
+
# harmonic-geometric mean (HGM)
|
5023
|
+
def LongMath.harmonic_geometric_mean(new_scale, rounding_mode, *args)
|
5024
|
+
result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(true, *args)
|
5025
|
+
if (all_same)
|
5026
|
+
return args[0].to_ld(new_scale, rounding_mode)
|
5027
|
+
end
|
5028
|
+
prec = (new_scale * 1.1 + 20).to_i
|
5029
|
+
x = harmonic_mean(prec, rounding_mode, *args)
|
5030
|
+
y = geometric_mean(prec, rounding_mode, *args)
|
5031
|
+
delta = 3*x.unit
|
5032
|
+
while ((x-y).abs >= delta)
|
5033
|
+
xn = harmonic_mean(prec, rounding_mode, x, y)
|
5034
|
+
yn = geometric_mean(prec, rounding_mode, x, y)
|
5035
|
+
x = xn
|
5036
|
+
y = yn
|
5037
|
+
end
|
5038
|
+
return x.round_to_scale(new_scale, rounding_mode)
|
5039
|
+
end
|
5040
|
+
|
5041
|
+
# quadratic mean
|
5042
|
+
def LongMath.quadratic_mean(new_scale, rounding_mode, *args)
|
5043
|
+
result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(true, *args)
|
5044
|
+
if (all_same)
|
5045
|
+
return args[0].to_ld(new_scale, rounding_mode)
|
5046
|
+
end
|
5047
|
+
sum = args.inject(0) do |psum, x|
|
5048
|
+
psum + x*x
|
5049
|
+
end
|
5050
|
+
quot = if (sum.kind_of? Integer)
|
5051
|
+
Rational(sum, args.size)
|
5052
|
+
else
|
5053
|
+
sum / args.size
|
5054
|
+
end
|
5055
|
+
result = LongMath.sqrt(quot, new_scale, rounding_mode)
|
5056
|
+
if (has_neg)
|
5057
|
+
result = -result
|
5058
|
+
end
|
5059
|
+
return result
|
5060
|
+
end
|
5061
|
+
|
5062
|
+
# cubic mean
|
5063
|
+
def LongMath.cubic_mean(new_scale, rounding_mode, *args)
|
5064
|
+
result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(false, *args)
|
5065
|
+
if (all_same)
|
5066
|
+
return args[0].to_ld(new_scale, rounding_mode)
|
5067
|
+
end
|
5068
|
+
sum = args.inject(0) do |psum, x|
|
5069
|
+
if (x.kind_of? Complex)
|
5070
|
+
raise ArgumentError, "cubic mean not supported for complex numbers args=#{args.inspect}"
|
5071
|
+
end
|
5072
|
+
psum + x*x*x
|
5073
|
+
end
|
5074
|
+
quot = if (sum.kind_of? Integer)
|
5075
|
+
Rational(sum, args.size)
|
5076
|
+
else
|
5077
|
+
sum / args.size
|
5078
|
+
end
|
5079
|
+
result = LongMath.cbrt(quot, new_scale, rounding_mode)
|
5080
|
+
return result
|
5081
|
+
end
|
5082
|
+
|
5083
|
+
private
|
5084
|
+
|
5085
|
+
# helper class for round_sum
|
5086
|
+
class RawElement
|
5087
|
+
include LongDecimalRoundingMode
|
5088
|
+
|
5089
|
+
def initialize(element, new_scale, rounding_mode=ROUND_FLOOR)
|
5090
|
+
@element = element
|
5091
|
+
@rounded = element.to_ld(new_scale, rounding_mode)
|
5092
|
+
@delta = @element-@rounded
|
5093
|
+
end
|
5094
|
+
|
5095
|
+
def add(epsilon)
|
5096
|
+
@rounded += epsilon
|
5097
|
+
end
|
5098
|
+
|
5099
|
+
attr_reader :element, :rounded, :delta
|
5100
|
+
|
5101
|
+
def <=>(other)
|
5102
|
+
delta <=> other.delta
|
5103
|
+
end
|
5104
|
+
end
|
5105
|
+
|
5106
|
+
public
|
5107
|
+
|
5108
|
+
# round elements in such a way that round(new_scale, rounding_mode_sum, sum(elements)) = sum(elements_rounded)
|
5109
|
+
# HAARE_NIEMEYER
|
5110
|
+
def LongMath.round_sum_hm(new_scale, rounding_mode_sum, *elements)
|
5111
|
+
if (elements.empty?)
|
5112
|
+
return elements
|
5113
|
+
end
|
5114
|
+
raw_sum = elements.inject(0) do |psum,x|
|
5115
|
+
psum + x
|
5116
|
+
end
|
5117
|
+
sum = raw_sum.to_ld(new_scale, rounding_mode_sum)
|
5118
|
+
raw_elements = elements.map do |element|
|
5119
|
+
RawElement.new(element, new_scale)
|
5120
|
+
end
|
5121
|
+
raw_elements_sum = raw_elements.inject(0) do |psum, element|
|
5122
|
+
psum + element.rounded
|
5123
|
+
end
|
5124
|
+
delta = sum - raw_elements_sum
|
5125
|
+
epsilon = LongDecimal(1, new_scale)
|
5126
|
+
raw_elements_sorted = raw_elements.sort.reverse
|
5127
|
+
n = (delta / epsilon).to_ld(0, ROUND_HALF_EVEN).to_i
|
5128
|
+
puts "delta=#{delta} epsilon=#{epsilon} n=#{n} sum=#{sum} raw_sum=#{raw_sum}"
|
5129
|
+
n.times do |i|
|
5130
|
+
raw_elements_sorted[i].add(epsilon)
|
5131
|
+
end
|
5132
|
+
result = raw_elements.map do |x|
|
5133
|
+
x.rounded
|
5134
|
+
end
|
5135
|
+
end
|
5136
|
+
|
5137
|
+
# round elements in such a way that round(new_scale, rounding_mode, sum(elements)) = sum(elements_rounded)
|
5138
|
+
# where rounding_mode_set is
|
5139
|
+
def LongMath.round_sum_divisor(new_scale, rounding_mode_sum, rounding_mode_set, *elements)
|
5140
|
+
if (elements.empty?)
|
5141
|
+
return elements
|
5142
|
+
end
|
5143
|
+
delta = -1
|
5144
|
+
raw_sum = elements.inject(0) do |psum,x|
|
5145
|
+
psum + x
|
5146
|
+
end
|
5147
|
+
sum = raw_sum.to_ld(new_scale, rounding_mode_sum)
|
5148
|
+
raw_elements = elements.map do |element|
|
5149
|
+
RawElement.new(element, new_scale, rounding_mode_set)
|
5150
|
+
end
|
5151
|
+
while (true)
|
5152
|
+
raw_elements_sum = raw_elements.inject(0) do |psum, element|
|
5153
|
+
psum + element.rounded
|
5154
|
+
end
|
5155
|
+
delta = sum - raw_elements_sum
|
5156
|
+
if (delta == 0)
|
5157
|
+
break
|
5158
|
+
end
|
5159
|
+
factor = (1 + delta/raw_elements_sum)
|
5160
|
+
puts("delta=#{delta} raw_elements_sum=#{raw_elements_sum} factor=#{factor}")
|
5161
|
+
raw_elements = raw_elements.map do |element|
|
5162
|
+
RawElement.new(element.element * factor, new_scale, rounding_mode_set)
|
5163
|
+
end
|
5164
|
+
end
|
5165
|
+
result = raw_elements.map do |x|
|
5166
|
+
x.rounded
|
5167
|
+
end
|
5168
|
+
end
|
5169
|
+
|
3702
5170
|
@@standard_mode = ROUND_HALF_UP
|
3703
5171
|
|
3704
5172
|
# default to be used as rounding mode when no explicit mode is provided
|