long-decimal 1.00.01 → 1.00.02

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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