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.
@@ -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.86 2011/01/23 20:55:32 bk1 Exp $
11
- # CVS-Label: $Name: RELEASE_1_00_01 $
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
- RoundingModeClass = Struct.new(:name, :num)
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
- ROUND_UP => ROUND_DOWN,
89
- ROUND_DOWN => ROUND_UP,
90
- ROUND_CEILING => ROUND_FLOOR,
91
- ROUND_FLOOR => ROUND_CEILING,
92
- ROUND_HALF_UP => ROUND_HALF_DOWN,
93
- ROUND_HALF_DOWN => ROUND_HALF_UP,
94
- ROUND_HALF_CEILING => ROUND_HALF_FLOOR,
95
- ROUND_HALF_FLOOR => ROUND_HALF_CEILING,
96
- ROUND_HALF_EVEN => ROUND_HALF_EVEN,
97
- ROUND_UNNECESSARY => ROUND_UNNECESSARY
98
- }
99
-
100
- # which mode is to be used instead when we do an additive inversion?
101
- ADD_INVERSE_MODE = {
102
- ROUND_UP => ROUND_UP,
103
- ROUND_DOWN => ROUND_DOWN,
104
- ROUND_CEILING => ROUND_FLOOR,
105
- ROUND_FLOOR => ROUND_CEILING,
106
- ROUND_HALF_UP => ROUND_HALF_UP,
107
- ROUND_HALF_DOWN => ROUND_HALF_DOWN,
108
- ROUND_HALF_CEILING => ROUND_HALF_FLOOR,
109
- ROUND_HALF_FLOOR => ROUND_HALF_CEILING,
110
- ROUND_HALF_EVEN => ROUND_HALF_EVEN,
111
- ROUND_UNNECESSARY => ROUND_UNNECESSARY
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(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 remainders.kind_of? Array
230
- raise TypeError, "remainders must be non-empty Array" unless remainders.length > 0
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, "ROUND_HALF_EVEN is not applicable here" if rounding_mode == LongDecimalRoundingMode::ROUND_HALF_EVEN
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
- # if (rounding_mode == LongDecimalRoundingMode::ROUND_FLOOR) then
275
- # return lower
276
- # elsif (rounding_mode == LongDecimalRoundingMode::ROUND_CEILING) then
277
- # return upper
278
- # end
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.sign
637
+ sign_self = self.sgn
281
638
  if (sign_self == 0) then
282
- if (rounding_mode == LongDecimalRoundingMode::ROUND_UP || rounding_mode == LongDecimalRoundingMode::ROUND_DOWN \
283
- || lower == -upper && (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_UP || rounding_mode == LongDecimalRoundingMode::ROUND_HALF_DOWN))
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
- # now we can assume that sign_self (and self) is != 0, which allows to decide on the rounding_mode
311
-
312
- if (rounding_mode == LongDecimalRoundingMode::ROUND_UP)
313
- # ROUND_UP goes to the closest possible value away from zero
314
- rounding_mode = (sign_self < 0) ? LongDecimalRoundingMode::ROUND_FLOOR : LongDecimalRoundingMode::ROUND_CEILING
315
- elsif (rounding_mode == LongDecimalRoundingMode::ROUND_DOWN)
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 (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_FLOOR) then
334
- rounding_mode = LongDecimalRoundingMode::ROUND_FLOOR
335
- elsif (rounding_mode == LongDecimalRoundingMode::ROUND_HALF_CEILING) then
336
- rounding_mode = LongDecimalRoundingMode::ROUND_CEILING
337
- else
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
- if (rounding_mode == LongDecimalRoundingMode::ROUND_FLOOR) then
343
- return lower
344
- elsif (rounding_mode == LongDecimalRoundingMode::ROUND_CEILING) then
345
- return upper
346
- else
347
- raise ArgumentError, "this case can never happen: rounding_mode=#{rounding_mode}"
348
- end
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.86 2011/01/23 20:55:32 bk1 Exp $-'
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
- divisor = divisor
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 == ROUND_HALF_CEILING)
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
- mode = (sign_quot > 0) ? ROUND_HALF_UP : ROUND_HALF_DOWN
532
-
533
- elsif (mode == ROUND_HALF_FLOOR)
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
- mode = (sign_quot < 0) ? ROUND_HALF_UP : ROUND_HALF_DOWN
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
- half = (abs_rem << 1) <=> divisor
546
- if (mode == ROUND_HALF_UP || mode == ROUND_HALF_DOWN || mode == ROUND_HALF_EVEN) then
547
- if (half < 0) then
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 half > 0 then
997
+ elsif direction_criteria > 0 then
550
998
  mode = ROUND_UP
551
999
  else
552
- # half == 0
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.86 2011/01/23 20:55:32 bk1 Exp $-'
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!(/\s/, "")
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.86 2011/01/23 20:55:32 bk1 Exp $-'
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 that new_scale.
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 = @numerator
2496
- den = @denominator
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 defaut values, in order to allow better testing.
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