long-decimal 1.00.01 → 1.00.02
Sign up to get free protection for your applications and to get access to all the features.
- 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
|