long-decimal 1.00.02 → 1.00.03

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a9b2ae9e4c8f0f4af956ca3603f351f2c45ecef1
4
- data.tar.gz: b0d237ee8813f6e5f2ac6d458b0f22a5afc3b9c4
3
+ metadata.gz: f4d758878cf5a3d2d5f20a4738e94a261114da04
4
+ data.tar.gz: 3db2e6b11a3f8aae44a7cccfc81ea902165ec4bb
5
5
  SHA512:
6
- metadata.gz: 40718a7b4ac4011aee592fd16516fe79ea39e1aaa585113b0220463057064ef14f7f934071e45313014427121862297fde386726bad73d6525ee530353b1ddd6
7
- data.tar.gz: 4ce22512fcba5dc1b57bd9dc389a269b52046f1d6e20ca134a5cc57aed34303ee9d5248249d84ce36d8e529549cad86b318f0d7eb6f2c3096ab0fc7ace9b58a9
6
+ metadata.gz: 8aa62b76ff524e76b9c2d38dbb5cd660c38dfd1fdae8f575312da56ecf7438875860fdd1fae605ba2dc04409fc252a67fcdb596117d04b2784e724657d20e425
7
+ data.tar.gz: 4dde6badc8da94abaef63c8151a13452dda84658a41bb83bd5efc219d36e3a03cb32b6999ec20ef28f91392d11b2dbba5dc7ddcb0ef32f06b5531e75cb346519
data/README CHANGED
@@ -1,11 +1,20 @@
1
1
  Version
2
2
  -------
3
3
 
4
- This version (1.00.02) is fixing bugs from version 1.00.01.
5
- It also can be considered as a release candidate from the upcoming
6
- version 1.01.00, adding new features.
4
+ This version (1.00.03) is fixing bugs from version 1.00.02.
5
+ It can be considered as a release candidate from the upcoming
6
+ version 1.01.00.
7
7
 
8
- Improvements over the previous version:
8
+ Change Log
9
+ ----------
10
+
11
+ Improvements in 1.00.03
12
+ - tests improved
13
+ - bug fixes in mean calculation functionality for non-LongDecimal paramters
14
+ - calculation of sqrt on arguments that result in the exact square root being the boundary for geometric or quadratic rounding recognized and fixed
15
+ - calculation of cbrt on arguments that result in the exact cube root being the boundary for cubic rounding recognized and fixed
16
+
17
+ Improvements over the previous version in 1.00.02
9
18
 
10
19
  - migration from CVS (rubyforge) to GIT (github)
11
20
  - bug fixed with frozen string attempted to be changed when parsing a string for a long-decimal number.
@@ -17,19 +26,29 @@ Improvements over the previous version:
17
26
  - moved power, log10, log2 etc. from long-decimal-extra.rb to long-decimal.rb
18
27
  - added more unit tests
19
28
 
29
+ Road Map
30
+ --------
31
+
20
32
  Work to do for 1.01.00:
21
33
  - go through functions that are working with rounding modes as parameter (like transcendental functions, sqrt, cbrt etc.) to ensure that they work properly with all new rounding modes
22
34
  - improve algorithm for rounding with sum constraint
23
35
  - go through unit tests to see they are covering all rounding modes
24
36
  - add unit tests for rounding with sum constraint
37
+ - caching of frequently used calculation results needs to be done independent of rounding mode
38
+ - add tests comparing results of transcendental functions for different rounding modes
25
39
  - rubydoc-documentation
26
40
  - external documentation
27
41
 
28
42
  Work to do for 1.02.00 (or 2.00.00):
29
43
  - add the following transcendental functions: sin, cos, tan, cot, sec, csc, sinh, cosh, tanh, coth, sech, csch, asin, acos, atan, atan2, acot, asec, acsc, asinh, acosh, atanh, acoth, asech, acsch
44
+ - support Complex of LongDecimal wherever it makes sense
30
45
  - documentation
31
46
  - external documentation
32
- - interoperability with flt
47
+ - interoperability with gmp, mpfr, mpfi, flt and other libraries
48
+ - interoparability with rails
49
+
50
+ Status
51
+ ------
33
52
 
34
53
  The existing functionality did not show any bugs during intensive
35
54
  testing, so it could be assumed that the whole library is good for
@@ -84,6 +103,11 @@ require 'long-decimal'
84
103
  Test
85
104
  ----
86
105
 
106
+ Tests have been run with Ruby 1.8.7, 1.9.3, 2.0.0 and 2.1.1
107
+ Some tests do not work with 1.8.7 because of missing language features.
108
+ They are skipped. No major effort for adapting tests to this version
109
+ will be allocated.
110
+
87
111
  Some runit tests have been included. They give some indication of the
88
112
  correctness of this library and allow changes to be checked in order
89
113
  to make sure that what was running before would still work afterwards.
@@ -99,27 +123,24 @@ ruby test/testlongdecimal.rb
99
123
 
100
124
  This is the result of the test:
101
125
 
102
- Finished in 5174.187655 seconds.
103
- 134 tests, 9461909 assertions, 0 failures, 0 errors
126
+ Finished in 2952.440454s, 0.0660 runs/s, 7759.3877 assertions/s.
127
+
128
+ 195 runs, 22909130 assertions, 0 failures, 0 errors, 0 skips
104
129
 
105
130
  In addition random tests for exp, exp2, exp10, sqrt, log, log2 and
106
131
  log10 can be run for a long time, using
107
132
 
108
133
  ruby test/testrandom.rb.
109
134
 
110
- The functionality in lib/long-decimal-extra.rb, which contains some
111
- more advanced and less tested methods, has its tests in
135
+ The power function did fail in some tests, which have been collected in
112
136
 
113
137
  ruby test/testlongdecimal-extra.rb
114
138
 
115
- and its random tests in
139
+ some additional tests are in
116
140
 
117
141
  ruby test/testrandom-extra.rb
118
142
 
119
- This is the result of the test:
120
-
121
- Finished in 4115.851979 seconds.
122
- 12 tests, 976 assertions, 0 failures, 0 errors
143
+ They should be integrated into testrandom.rb
123
144
 
124
145
  Likewise tests for powers x to the yth with random x and y (which also
125
146
  resided in long-decimal-extra.rb) can be tested for a long time using
@@ -137,11 +158,10 @@ long-decimal-extra and find an error with it, please report it.
137
158
  Install
138
159
  -------
139
160
 
140
-
141
161
  1. Using ruby-gems (preferred)
142
162
  - open a shell window
143
163
  - become root, unless the current user has the right to install gems
144
- (which is usually the case on windows)
164
+ (which is usually the case on windows or when using rvm)
145
165
 
146
166
  su
147
167
 
@@ -162,25 +182,21 @@ Install
162
182
  where $RUBY_DIR is the directory containing your ruby-installation,
163
183
  usually /usr/lib/ruby or /usr/local/lib/ruby on
164
184
  Linux/Unix-systems.
165
- $RUBY_VERSION is the major version of your Ruby, like 1.8
185
+ $RUBY_VERSION is the major version of your Ruby, like 2.1
166
186
  $LONG_DECIMAL_VERSION is the version of long-decimal that you
167
- have installed, like 0.00.20
168
- on my machine that would be
169
- /usr/local/lib/ruby/gems/1.8/doc/long-decimal-0.02.01/rdoc/index.html
187
+ have installed, like 1.00.03
170
188
 
171
189
  2. Installing from the sources (it is preferred to use the
172
190
  gem-installation, but since long-decimal is open-source-software you
173
191
  are off course granted the right to download the source and change
174
192
  it and install your own version.)
175
193
 
176
- - download the newest source-tar.gz-file from long-decimal project at rubyforge
194
+ - download the newest source-tar.gz-file from long-decimal project at github
177
195
  which can be found by
178
- http://rubyforge.org/projects/long-decimal/ -> Files
179
- ( http://rubyforge.org/frs/?group_id=1334 )
180
196
  - open a shell window
181
197
  cd to the directory where you have downloaded the .tar.gz-file
182
198
  unpack the file using tar
183
- tar xfzvv long-decimal-beta-1_00.tar.gz
199
+ tar xfzvv long-decimal-*.tar.gz
184
200
  cd long-decimal
185
201
  - now you can use rake for several operations
186
202
  - rake test
@@ -238,7 +254,7 @@ License
238
254
 
239
255
  Ruby's license or LGPL
240
256
  Find copies of these licenses on http://www.gnu.org/ or http://www.ruby-lang.org/
241
- � Karl Brodowsky (IT Sky Consulting GmbH) 2006-2014
257
+ � Karl Brodowsky (IT Sky Consulting GmbH) 2006-2015
242
258
  http://www.it-sky-consulting.com/
243
259
 
244
260
  Warranty
data/Rakefile CHANGED
@@ -3,8 +3,9 @@
3
3
  #
4
4
  # Rakefile for long-decimal project
5
5
  #
6
- # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
6
+ # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2015
7
7
  #
8
+ # TAG: $TAG v1.00.03$
8
9
  # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/Rakefile,v 1.4 2009/04/15 19:29:37 bk1 Exp $
9
10
  # CVS-Label: $Name: $
10
11
  # Author: $Author: bk1 $ (Karl Brodowsky)
@@ -1,8 +1,13 @@
1
1
  #
2
2
  # long-decimal-extra.rb -- Arbitrary precision decimals with fixed decimal point
3
3
  #
4
- # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
4
+ # additional features: all functionality in this file is experimental
5
+ # and can be removed without notice in future versions. Or moved to
6
+ # long-decimal.rb, if useful.
5
7
  #
8
+ # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2015
9
+ #
10
+ # TAG: $TAG v1.00.03$
6
11
  # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/lib/long-decimal-extra.rb,v 1.29 2011/02/04 23:17:21 bk1 Exp $
7
12
  # CVS-Label: $Name: $
8
13
  # Author: $Author: bk1 $ (Karl Brodowsky)
@@ -320,12 +325,14 @@ class LongDecimal
320
325
  elsif dividend < divisor
321
326
  # self is between 0 and 1 and dividend > LongMath::MAX_FLOATABLE
322
327
  # return LongDecimal(dividend.div(LongMath.npower10(digits)), scale -digits).to_f
328
+ # puts "via s (1): #{self.inspect}"
323
329
  return self.to_s.to_f
324
330
  else
325
331
  q = dividend.abs / divisor
326
332
  if (q.abs > 1000000000000000000000)
327
333
  return q.to_f
328
334
  else
335
+ # puts "via s (2): #{self.inspect}"
329
336
  return self.to_s.to_f
330
337
  end
331
338
  end
@@ -456,4 +463,33 @@ class Bignum
456
463
 
457
464
  end
458
465
 
466
+ def LongMath.continued_fraction(x, steps)
467
+ if (x == 0)
468
+ x
469
+ end
470
+ arr = []
471
+ steps.times do
472
+ xi = x.to_ld(0, LongMath::ROUND_FLOOR)
473
+ xd = x-xi
474
+ if xd.zero?
475
+ return arr
476
+ end
477
+ x = 1/xd
478
+ arr.push(xi.to_i)
479
+ end
480
+ arr
481
+ end
482
+
483
+ def LongMath.continued_fraction_to_r(arr)
484
+ result = nil
485
+ arr.reverse.each do |x|
486
+ if (result.nil?)
487
+ result = Rational(x)
488
+ else
489
+ result = Rational(x) + 1/result
490
+ end
491
+ end
492
+ result
493
+ end
494
+
459
495
  # end of file long-decimal-extra.rb
@@ -1,12 +1,13 @@
1
1
  #
2
2
  # long-decimal.rb -- Arbitrary precision decimals with fixed decimal point
3
3
  #
4
- # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
4
+ # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2015
5
5
  #
6
6
  # This class contains the basic functionality for working with LongDecimal
7
7
  # additional functionality, mostly transcendental functions,
8
8
  # may be found in long-decimal-extra.rb
9
9
  #
10
+ # TAG: $TAG v1.00.03$
10
11
  # 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
12
  # CVS-Label: $Name: $
12
13
  # Author: $Author: bk1 $ (Karl Brodowsky)
@@ -50,6 +51,8 @@ module LongDecimalRoundingMode
50
51
  private
51
52
 
52
53
  ALL_MINOR = [ MINOR_UP, MINOR_DOWN, MINOR_CEILING, MINOR_FLOOR, MINOR_EVEN, MINOR_ODD ]
54
+ # puts(ALL_MINOR)
55
+ # puts
53
56
 
54
57
  NO_MINOR = [ MINOR_UNUSED ]
55
58
 
@@ -105,6 +108,9 @@ module LongDecimalRoundingMode
105
108
 
106
109
  ALL_MAJOR_MODES = [ MAJOR_UP, MAJOR_DOWN, MAJOR_CEILING, MAJOR_FLOOR, MAJOR_UNNECESSARY, MAJOR_HALF, MAJOR_GEOMETRIC, MAJOR_HARMONIC, MAJOR_QUADRATIC, MAJOR_CUBIC ]
107
110
 
111
+ # puts("ALL_MAJOR_MODES:")
112
+ # puts(ALL_MAJOR_MODES)
113
+ # puts
108
114
 
109
115
  MUL_INVERSE_MAJOR_MODE = {
110
116
  MAJOR_UP => MAJOR_DOWN,
@@ -298,9 +304,9 @@ module LongDecimalRoundingMode
298
304
  on_boundary = true
299
305
  end
300
306
  elsif (major == MAJOR_CUBIC)
301
- cube = unrounded * unrounded * unrounded
307
+ cube = unrounded ** 3
302
308
  lhs = 2 * cube
303
- rhs = lower * lower * lower + upper * upper * upper
309
+ rhs = lower ** 3 + upper ** 3
304
310
  d = lhs <=> rhs
305
311
  if (d < 0)
306
312
  # unrounded < x_cubic(lower, upper)
@@ -382,7 +388,9 @@ module LongDecimalRoundingMode
382
388
  end
383
389
 
384
390
  ALL_ROUNDING_MODES.freeze
391
+ # puts(ALL_ROUNDING_MODES)
385
392
  MODE_LOOKUP.freeze
393
+ # puts(MODE_LOOKUP)
386
394
 
387
395
  ALL_ROUNDING_MODES.each do |rm|
388
396
  majm = rm.major
@@ -391,6 +399,7 @@ module LongDecimalRoundingMode
391
399
  MUL_INVERSE_MODE[rm] = mul_inv
392
400
  add_inv = MODE_LOOKUP[[ ADD_INVERSE_MAJOR_MODE[majm], ADD_INVERSE_MINOR_MODE[minm]]]
393
401
  ADD_INVERSE_MODE[rm] = add_inv
402
+ # puts("rm=#{rm} mul_inv=#{mul_inv} add_inv=#{add_inv}")
394
403
  end
395
404
 
396
405
  MUL_INVERSE_MODE.freeze
@@ -614,6 +623,7 @@ class Integer
614
623
  end
615
624
  lower = self - (r_self - r_lower)
616
625
  upper = self + (r_upper - r_self)
626
+ # puts "round_to_allowed_remainders(remainders_param=#{remainders_param} modulus=#{modulus} rounding_mode=#{rounding_mode} zero_rounding_mode=#{zero_rounding_mode}) remainders=#{remainders} lower=#{lower} upper=#{upper} r_lower=#{r_lower} r_upper=#{r_upper} r_self=#{r_self}"
617
627
 
618
628
  unless (lower < self && self < upper)
619
629
  raise ArgumentError, "self=#{self} not in (#{lower}, #{upper}) with r_self=#{r_self} r_lower=#{r_lower} r_upper=#{r_upper}"
@@ -1440,6 +1450,7 @@ class LongDecimal < LongDecimalBase
1440
1450
  # result 0.0 if int_val == 0
1441
1451
  if (int_val == 0)
1442
1452
  # t1
1453
+ # puts "t1 #{self.to_s}=#{self.inspect} -> 0.0"
1443
1454
  return 0.0
1444
1455
  end
1445
1456
 
@@ -1447,12 +1458,15 @@ class LongDecimal < LongDecimalBase
1447
1458
  if (int_val.abs <= LongMath::MAX_FLOATABLE)
1448
1459
  y = int_val.to_f
1449
1460
  # t2
1461
+ # puts "t2 #{self.to_s}=#{self.inspect} -> #{y}"
1450
1462
  return y
1451
1463
  elsif int_val < 0
1452
1464
  # t13
1465
+ # puts "t13 #{self.to_s}=#{self.inspect} -> -Infinity"
1453
1466
  return -1.0/0.0
1454
1467
  elsif int_val > 0
1455
1468
  # t14
1469
+ # puts "t13 #{self.to_s}=#{self.inspect} -> Infinity"
1456
1470
  return 1.0/0.0
1457
1471
  end
1458
1472
  end
@@ -1461,6 +1475,7 @@ class LongDecimal < LongDecimalBase
1461
1475
  if (self < 0) then
1462
1476
  y = -(-self).to_f
1463
1477
  # t3
1478
+ # puts "t3 #{self.to_s}=#{self.inspect} -> #{y}"
1464
1479
  return y
1465
1480
  end
1466
1481
 
@@ -1468,6 +1483,7 @@ class LongDecimal < LongDecimalBase
1468
1483
  if (int_val <= LongMath::MAX_FLOATABLE && scale <= Float::MAX_10_EXP)
1469
1484
  y = to_f_simple(int_val, scale)
1470
1485
  # t4
1486
+ # puts "t4 #{self.to_s}=#{self.inspect} -> #{y}"
1471
1487
  return y
1472
1488
  end
1473
1489
 
@@ -1477,6 +1493,7 @@ class LongDecimal < LongDecimalBase
1477
1493
  # handle overflow: raise exception
1478
1494
  if (int_val > divisor * LongMath::MAX_FLOATABLE) then
1479
1495
  # t5
1496
+ # puts "t5 #{self.to_s}=#{self.inspect} -> Infinity"
1480
1497
  return 1/0.0 # Infinity
1481
1498
  end
1482
1499
 
@@ -1484,6 +1501,7 @@ class LongDecimal < LongDecimalBase
1484
1501
  # handle underflow: return 0.0
1485
1502
  if (int_val * LongMath::INV_MIN_FLOATABLE * 20 < divisor) then
1486
1503
  # t6
1504
+ # puts "t6 #{self.to_s}=#{self.inspect} -> 0.0"
1487
1505
  p = int_val * LongMath::INV_MIN_FLOATABLE * 20
1488
1506
  d = divisor
1489
1507
  n = int_val
@@ -1503,11 +1521,13 @@ class LongDecimal < LongDecimalBase
1503
1521
  y *= q
1504
1522
  if (y == 0.0)
1505
1523
  # t7
1524
+ # puts "t7 #{self.to_s}=#{self.inspect} -> #{y}"
1506
1525
  return y
1507
1526
  end
1508
1527
  s -= qe
1509
1528
  end
1510
1529
  # t8
1530
+ # puts "t8 #{self.to_s}=#{self.inspect} -> #{y}"
1511
1531
  return y
1512
1532
  end
1513
1533
 
@@ -1520,6 +1540,7 @@ class LongDecimal < LongDecimalBase
1520
1540
  # scale = 0, 0 < int_val <= MAX_FLOATABLE
1521
1541
  y = to_f_simple(rounded_ld.int_val, rounded_ld.scale)
1522
1542
  # t9
1543
+ # puts "t9 #{self.to_s}=#{self.inspect} #{rounded_ld.to_s}=#{rounded_ld.inspect} -> #{y}"
1523
1544
  return y
1524
1545
  end
1525
1546
 
@@ -1530,6 +1551,7 @@ class LongDecimal < LongDecimalBase
1530
1551
 
1531
1552
  if (cmp == 0)
1532
1553
  # t10
1554
+ # puts "t10 #{self.to_s}=#{self.inspect} -> 1.0"
1533
1555
  return 1.0
1534
1556
  elsif (cmp > 0)
1535
1557
  # self > 1, retain MAX_SIGNIFICANT_FLOATABLE_DIGITS
@@ -1537,6 +1559,7 @@ class LongDecimal < LongDecimalBase
1537
1559
  # self >= LongMath::FLOATABLE_WITHOUT_FRACTION > self > 1, scale = 16,
1538
1560
  y = to_f_simple(rounded_ld.int_val, rounded_ld.scale)
1539
1561
  # t11
1562
+ # puts "t11 #{self.to_s}=#{self.inspect} #{rounded_ld.to_s}=#{rounded_ld.inspect} -> #{y}"
1540
1563
  return y
1541
1564
  else
1542
1565
  # self < 1
@@ -1552,6 +1575,7 @@ class LongDecimal < LongDecimalBase
1552
1575
  rounded_ld = round_to_scale(new_scale, ROUND_HALF_UP)
1553
1576
  y = to_f_simple(rounded_ld.int_val, rounded_ld.scale)
1554
1577
  # t12
1578
+ # puts "t12 #{self.to_s}=#{self.inspect} #{rounded_ld.to_s}=#{rounded_ld.inspect} sd=#{sd} #{self <=> 1} -> #{y}"
1555
1579
  return y
1556
1580
  end
1557
1581
  end
@@ -1668,11 +1692,21 @@ class LongDecimal < LongDecimalBase
1668
1692
  #
1669
1693
  def sint_digits10
1670
1694
  if (@digits10.nil?)
1671
- @digits10 = LongMath.int_digits10(int_val) - scale
1695
+ result = LongMath.int_digits10(int_val) - scale
1696
+ if frozen?
1697
+ return result
1698
+ end
1699
+ @digits10 = result
1672
1700
  end
1673
1701
  @digits10
1674
1702
  end
1675
1703
 
1704
+ # freeze but calcuate sint_digits10 before
1705
+ def freeze_ld
1706
+ sint_digits10
1707
+ freeze
1708
+ end
1709
+
1676
1710
  #
1677
1711
  # number of decimal digits before the decimal point, not counting a
1678
1712
  # single 0.
@@ -2191,9 +2225,11 @@ class LongDecimal < LongDecimalBase
2191
2225
  # s, o = other.coerce(self.to_f)
2192
2226
  if (RUNNING_19)
2193
2227
  s, o = other.coerce(self)
2228
+ # puts "complex coerce 19: #{self}, #{other} -> #{s}, #{o}"
2194
2229
  return o, s
2195
2230
  else
2196
2231
  s, o = other.coerce(Complex(self, 0))
2232
+ # puts "complex coerce 18/J: #{self}, #{other} -> #{s}, #{o}"
2197
2233
  return o, s
2198
2234
  end
2199
2235
  # s, o = other.coerce(Complex(self.to_f, 0))
@@ -2529,6 +2565,7 @@ class LongDecimalQuot < LongDecimalBase
2529
2565
  if (s.kind_of? LongDecimalQuot) then
2530
2566
  LongDecimalQuot(s.rat - o.rat, [s.scale, o.scale].max)
2531
2567
  else
2568
+ # puts "ldq-coerce: s=#{s} o=#{o} self=#{self} other=#{other}"
2532
2569
  s - o
2533
2570
  end
2534
2571
  end
@@ -2803,6 +2840,9 @@ class Numeric
2803
2840
  # optional second argument gives the rouding mode
2804
2841
  #
2805
2842
  def to_ld(prec = nil, mode = LongMath.standard_mode)
2843
+ if (kind_of? Complex)
2844
+ return Complex(real.to_ld(prec, mode), imaginary.to_ld(prec, mode))
2845
+ end
2806
2846
  l = LongDecimal(self)
2807
2847
  if (prec.nil?)
2808
2848
  return l
@@ -2937,7 +2977,9 @@ class Rational
2937
2977
  def to_f
2938
2978
  num = numerator
2939
2979
  den = denominator
2980
+ # puts("num=#{num} den=#{den}")
2940
2981
  sign = num <=> 0
2982
+ # puts("num=#{num} den=#{den} sign=#{sign}")
2941
2983
  if (sign.zero?)
2942
2984
  return 0.0
2943
2985
  elsif sign < 0
@@ -3022,7 +3064,9 @@ module LongMath
3022
3064
  if (power.nil?)
3023
3065
  power = powers_big_base ** n1
3024
3066
  @@powers_of_big_base[n1] = power
3067
+ # puts "npower_of_big_base(n1=#{n1}) c->#{power.size}"
3025
3068
  else
3069
+ # puts "npower_of_big_base(n1=#{n1}) c<-#{power.size}"
3026
3070
  end
3027
3071
  power
3028
3072
  end
@@ -3173,6 +3217,21 @@ module LongMath
3173
3217
  return
3174
3218
  end
3175
3219
  end
3220
+ key.freeze
3221
+ if (val.kind_of? LongDecimal)
3222
+ val.freeze_ld
3223
+ elsif (val.kind_of? Array)
3224
+ val.each do |entry|
3225
+ if (entry.kind_of? LongDecimal)
3226
+ entry.freeze_ld
3227
+ else
3228
+ entry.freeze
3229
+ end
3230
+ end
3231
+ val.freeze
3232
+ else
3233
+ val.freeze
3234
+ end
3176
3235
  @@cache[key] = val
3177
3236
  end
3178
3237
  end
@@ -3244,8 +3303,11 @@ module LongMath
3244
3303
  n1 = n >> POWERS_MED_EXP_PARAM
3245
3304
  p = npower10_cached(n0)
3246
3305
  if (n1 > 0) then
3306
+ # puts "n0=#{n0} n1=#{n1}"
3247
3307
  p1 = npower_of_big_base(n1)
3308
+ # puts "n0=#{n0} n1=#{n1} p1"
3248
3309
  p *= p1
3310
+ # puts "n0=#{n0} n1=#{n1} p"
3249
3311
  end
3250
3312
  return p
3251
3313
  end
@@ -4031,7 +4093,7 @@ module LongMath
4031
4093
  end
4032
4094
  cache_key = nil
4033
4095
  y_arr = nil
4034
- unless (with_rem)
4096
+ unless (with_rem || mode.major == MAJOR_GEOMETRIC || mode.major == MAJOR_QUADRATIC)
4035
4097
  cache_key = get_cache_key("sqrt", x, mode, [2, 3, 5, 6, 7, 8, 10])
4036
4098
  y_arr = get_cached(cache_key, x, prec)
4037
4099
  end
@@ -4046,8 +4108,43 @@ module LongMath
4046
4108
  return y_arr
4047
4109
  else
4048
4110
  y, r = y_arr
4049
- if ((mode == ROUND_HALF_EVEN || mode == ROUND_HALF_ODD || mode == ROUND_HALF_DOWN) && r > 0) then
4050
- mode = ROUND_HALF_UP
4111
+ if (mode.major == MAJOR_GEOMETRIC || mode.major == MAJOR_QUADRATIC)
4112
+ # we need to deal with the case that the square root is on the exact border
4113
+ y_lower = y.round_to_scale(prec, ROUND_DOWN)
4114
+ y_upper = y.round_to_scale(prec, ROUND_UP)
4115
+ # puts "mode=#{mode} x=#{x} y=#{y} r=#{r} y_lower=#{y_lower} y_upper=#{y_upper} prec=#{prec} prec1=#{prec1}"
4116
+ if (r == 0)
4117
+ if (y == y_lower)
4118
+ # puts "r=0 y=y_lower=#{y_lower} (x=#{x} y_upper=#{y_upper} mode=#{mode})"
4119
+ return y_lower
4120
+ elsif (y == y_upper)
4121
+ # puts "r=0 y=y_upper=#{y_upper} (x=#{x} y_lower=#{y_lower} mode=#{mode})"
4122
+ return y_upper
4123
+ end
4124
+ end
4125
+ # puts "mode=#{mode} x=#{x} gm2=#{y_lower * y_upper} qm2=#{arithmetic_mean(prec*2+1, ROUND_HALF_UP, y_lower*y_lower, y_upper*y_upper)}"
4126
+ if (mode.major == MAJOR_GEOMETRIC && x == y_lower * y_upper \
4127
+ || mode.major == MAJOR_QUADRATIC && x == arithmetic_mean(prec1*2+1, ROUND_HALF_UP, y_lower*y_lower, y_upper*y_upper))
4128
+ if (mode.minor == MINOR_UP || mode.minor == MINOR_CEILING)
4129
+ # puts "r=0 y=#{y} on boundary: y_upper=#{y_upper} (x=#{x} y_lower=#{y_lower} mode=#{mode})"
4130
+ return y_upper;
4131
+ elsif (mode.minor == MINOR_DOWN || mode.minor == MINOR_FLOOR)
4132
+ # puts "r=0 y=#{y} on boundary: y_lower=#{y_lower} (x=#{x} y_upper=#{y_upper} mode=#{mode})"
4133
+ return y_lower;
4134
+ elsif (mode.minor == MINOR_EVEN && y_upper[0] == 0 || mode.minor == MINOR_ODD && y_upper[0] == 1)
4135
+ # puts "r=0 y=#{y} on boundary odd/even: y_upper=#{y_upper} (x=#{x} y_lower=#{y_lower} y_upper=#{y_upper} mode=#{mode})"
4136
+ return y_upper;
4137
+ elsif (mode.minor == MINOR_EVEN && y_lower[0] == 0 || mode.minor == MINOR_ODD && y_lower[0] == 1)
4138
+ # puts "r=0 y=#{y} on boundary odd/even: y_lower=#{y_lower} (x=#{x} y_lower=#{y_lower} y_upper=#{y_upper} mode=#{mode})"
4139
+ return y_lower;
4140
+ else
4141
+ raise ArgumentError, "unsupported combination: x=#{x} prec=#{prec} prec1=#{prec1} mode=#{mode} y=#{y} r=#{r} y_lower=#{y_lower} y_upper=#{y_upper}"
4142
+ end
4143
+ end
4144
+ end
4145
+
4146
+ if ((mode.minor == MINOR_EVEN || mode.minor == MINOR_ODD || mode.minor == MINOR_DOWN || mode.minor == MINOR_FLOOR) && r > 0) then
4147
+ mode = MODE_LOOKUP[[mode.major, MINOR_UP]];
4051
4148
  end
4052
4149
  y = y.round_to_scale(prec, mode)
4053
4150
  return y
@@ -4099,7 +4196,7 @@ module LongMath
4099
4196
  end
4100
4197
  cache_key = nil
4101
4198
  y_arr = nil
4102
- unless (with_rem)
4199
+ unless (with_rem || mode.major == MAJOR_CUBIC)
4103
4200
  cache_key = get_cache_key("cbrt", x, mode, [2, 3, 5, 6, 7, 8, 10])
4104
4201
  y_arr = get_cached(cache_key, x, prec)
4105
4202
  end
@@ -4114,8 +4211,42 @@ module LongMath
4114
4211
  return y_arr
4115
4212
  else
4116
4213
  y, r = y_arr
4117
- if ((mode == ROUND_HALF_EVEN || mode == ROUND_HALF_ODD || mode == ROUND_HALF_DOWN) && r > 0) then
4118
- mode = ROUND_HALF_UP
4214
+ if (mode.major == MAJOR_CUBIC)
4215
+ # we need to deal with the case that the square root is on the exact border
4216
+ y_lower = y.round_to_scale(prec, ROUND_DOWN)
4217
+ y_upper = y.round_to_scale(prec, ROUND_UP)
4218
+ # puts "mode=#{mode} x=#{x} y=#{y} r=#{r} y_lower=#{y_lower} y_upper=#{y_upper} prec=#{prec} prec1=#{prec1}"
4219
+ if (r == 0)
4220
+ if (y == y_lower)
4221
+ # puts "r=0 y=y_lower=#{y_lower} (x=#{x} y_upper=#{y_upper} mode=#{mode})"
4222
+ return y_lower
4223
+ elsif (y == y_upper)
4224
+ # puts "r=0 y=y_upper=#{y_upper} (x=#{x} y_lower=#{y_lower} mode=#{mode})"
4225
+ return y_upper
4226
+ end
4227
+ end
4228
+ # puts "mode=#{mode} x=#{x} gm2=#{y_lower * y_upper} qm2=#{arithmetic_mean(prec*2+1, ROUND_HALF_UP, y_lower*y_lower, y_upper*y_upper)}"
4229
+ if (mode.major == MAJOR_CUBIC && x == arithmetic_mean(prec1*3+1, ROUND_HALF_UP, y_lower.cube(), y_upper.cube()))
4230
+ if (mode.minor == MINOR_UP || mode.minor == MINOR_CEILING)
4231
+ # puts "r=0 y=#{y} on boundary: y_upper=#{y_upper} (x=#{x} y_lower=#{y_lower} mode=#{mode})"
4232
+ return y_upper;
4233
+ elsif (mode.minor == MINOR_DOWN || mode.minor == MINOR_FLOOR)
4234
+ # puts "r=0 y=#{y} on boundary: y_lower=#{y_lower} (x=#{x} y_upper=#{y_upper} mode=#{mode})"
4235
+ return y_lower;
4236
+ elsif (mode.minor == MINOR_EVEN && y_upper[0] == 0 || mode.minor == MINOR_ODD && y_upper[0] == 1)
4237
+ # puts "r=0 y=#{y} on boundary odd/even: y_upper=#{y_upper} (x=#{x} y_lower=#{y_lower} y_upper=#{y_upper} mode=#{mode})"
4238
+ return y_upper;
4239
+ elsif (mode.minor == MINOR_EVEN && y_lower[0] == 0 || mode.minor == MINOR_ODD && y_lower[0] == 1)
4240
+ # puts "r=0 y=#{y} on boundary odd/even: y_lower=#{y_lower} (x=#{x} y_lower=#{y_lower} y_upper=#{y_upper} mode=#{mode})"
4241
+ return y_lower;
4242
+ else
4243
+ raise ArgumentError, "unsupported combination: x=#{x} prec=#{prec} prec1=#{prec1} mode=#{mode} y=#{y} r=#{r} y_lower=#{y_lower} y_upper=#{y_upper}"
4244
+ end
4245
+ end
4246
+ end
4247
+
4248
+ if ((mode.minor == MINOR_EVEN || mode.minor == MINOR_ODD || mode.minor == MINOR_DOWN || mode.minor == MINOR_FLOOR) && r > 0) then
4249
+ mode = MODE_LOOKUP[[mode.major, MINOR_UP]];
4119
4250
  end
4120
4251
  y = y.round_to_scale(prec, mode)
4121
4252
  return y
@@ -4661,6 +4792,7 @@ module LongMath
4661
4792
  raise TypeError, "x=#{x.inspect} must not negative" unless x >= 0
4662
4793
  prec = check_is_prec(prec, "prec")
4663
4794
  check_is_mode(mode, "mode")
4795
+ # puts "LongMath.power(x=#{x} y=#{y} prec=#{prec} mode=#{mode})"
4664
4796
 
4665
4797
  # handle the special cases where base or exponent are 0 or 1 explicitely
4666
4798
  if y.zero? then
@@ -4677,8 +4809,10 @@ module LongMath
4677
4809
  # x ** y <= 10**-s/2 <=> y * log(x) <= -s log(10) - log(2)
4678
4810
 
4679
4811
  iprec, iprec_x, iprec_y, logx_y_f = calc_iprec_for_power(x, y, prec)
4812
+ # puts "x=#{x} y=#{y} prec=#{prec} iprec=#{iprec} iprec_x=#{iprec_x} iprec_y=#{iprec_y} logx_y_f=#{logx_y_f}: checking x < 1 && y > 0 || x > 1 && y < 0=#{x < 1 && y > 0 || x > 1 && y < 0}"
4680
4813
  $stdout.flush
4681
4814
  if (x < 1 && y > 0 || x > 1 && y < 0) then
4815
+ # puts "checking if zero logx_y_f=#{logx_y_f} <= #{- prec * LOG10 - LOG2}"
4682
4816
  if (logx_y_f <= - prec * LOG10 - LOG2) then
4683
4817
  return LongDecimal.zero!(prec)
4684
4818
  end
@@ -4728,8 +4862,10 @@ module LongMath
4728
4862
  y = -y
4729
4863
  x = (1/x).round_to_scale(iprec_x*2, mode)
4730
4864
  iprec, iprec_x, iprec_y, logx_y_f = calc_iprec_for_power(x, y, prec)
4865
+ # puts "x=#{x} y=#{y} prec=#{prec} iprec=#{iprec} iprec_x=#{iprec_x} iprec_y=#{iprec_y} logx_y_f=#{logx_y_f}: checking x < 1 && y > 0 || x > 1 && y < 0=#{x < 1 && y > 0 || x > 1 && y < 0}"
4731
4866
  $stdout.flush
4732
4867
  if (x < 1 && y > 0 || x > 1 && y < 0) then
4868
+ # puts "checking if zero logx_y_f=#{logx_y_f} <= #{- prec * LOG10 - LOG2}"
4733
4869
  if (logx_y_f <= - prec * LOG10 - LOG2) then
4734
4870
  return LongDecimal.zero!(prec)
4735
4871
  end
@@ -4741,12 +4877,14 @@ module LongMath
4741
4877
  y0 = y.round_to_scale(0, LongMath.standard_imode).to_i
4742
4878
  x0 = x
4743
4879
  point_shift = 0
4880
+ # puts "x0=#{x0} y0=#{y0}"
4744
4881
  while x0 > LongMath::MAX_FLOATABLE
4745
4882
  x0 = x0.move_point_left(100)
4746
4883
  point_shift += 100
4747
4884
  end
4748
4885
  iprec2 = 2 * (iprec + point_shift)
4749
4886
  iprec3 = [ iprec2, LongMath.prec_limit() - 24 ].min
4887
+ # puts "x0=#{x0} y0=#{y0} point_shift=#{point_shift} iprec=#{iprec} iprec2=#{iprec2} iprec3=#{iprec3}"
4750
4888
  z0 = LongMath.ipower(x0, y0, iprec3, mode)
4751
4889
  if (point_shift > 0)
4752
4890
  unless z0.kind_of? LongDecimal
@@ -4762,6 +4900,7 @@ module LongMath
4762
4900
  # z1 = LongMath.power_internal(x, y1, prec+prec_extra , mode)
4763
4901
  z1 = LongMath.power_internal(x, y1, prec+prec_extra + 4, mode)
4764
4902
  z = z0 * z1
4903
+ # puts("x=#{x} y=#{y} z=#{z} y not int")
4765
4904
  return z.to_ld(prec, mode)
4766
4905
  end
4767
4906
 
@@ -4780,10 +4919,12 @@ module LongMath
4780
4919
  raise TypeError, "exponent y=#{y.inspect} must not be greater MAX_FLOATABLE=#{MAX_FLOATABLE}" unless y.abs <= MAX_FLOATABLE
4781
4920
  prec = check_is_prec(prec, "prec")
4782
4921
  check_is_mode(mode, "mode")
4922
+ # puts "LongMath.ipower(x=#{x} y=#{y} prec=#{prec} mode=#{mode})"
4783
4923
 
4784
4924
  if (y.zero?)
4785
4925
  return 1
4786
4926
  elsif ! (x.kind_of? LongDecimalBase) || x.scale * y.abs <= prec
4927
+ # puts "x=#{x} y=#{y} using **"
4787
4928
  return x ** y
4788
4929
  elsif (y < 0)
4789
4930
  l = Math.log10(x.abs.to_f)
@@ -4792,10 +4933,12 @@ module LongMath
4792
4933
  end
4793
4934
  # return (1/LongMath.ipower(x, -y, prec + 2, mode)).round_to_scale(prec, mode)
4794
4935
  xi = 1/x
4936
+ # puts "x=#{x} y=#{y} prec=#{prec} using (1/x)**y xi=#{xi}"
4795
4937
  xr = xi.round_to_scale(prec + 6, mode)
4796
4938
  return LongMath.ipower(xr, -y, prec, mode)
4797
4939
  else
4798
4940
  # y > 0
4941
+ # puts "x=#{x} y=#{y} regular"
4799
4942
  cnt = 0
4800
4943
  z = x
4801
4944
  y0 = y
@@ -4816,6 +4959,7 @@ module LongMath
4816
4959
  x = x.round_to_scale(prec+4, mode)
4817
4960
  end
4818
4961
  if (cnt > 1000)
4962
+ # puts("ipower x=#{x} y=#{y} cnt=#{cnt} z=#{z} t=#{Time.now - t0}")
4819
4963
  cnt = 0
4820
4964
  end
4821
4965
 
@@ -4828,7 +4972,9 @@ module LongMath
4828
4972
  end
4829
4973
  end
4830
4974
  end
4975
+ # puts "z=#{z} rounding prec=#{prec}"
4831
4976
  z = z.round_to_scale(prec, mode)
4977
+ # puts "rounded -> z=#{z}"
4832
4978
  return z
4833
4979
  end
4834
4980
  end
@@ -4934,7 +5080,7 @@ module LongMath
4934
5080
  if (args.empty?)
4935
5081
  raise ArgumentError, "cannot calculate average of empty array"
4936
5082
  end
4937
- sum = args.inject(0) do |psum,x|
5083
+ sum = args.inject(LongDecimal.zero!((1.5*new_scale + 25).to_i)) do |psum,x|
4938
5084
  psum + x
4939
5085
  end
4940
5086
  raw_result = if (sum.kind_of? Integer)
@@ -4943,6 +5089,21 @@ module LongMath
4943
5089
  sum / args.size
4944
5090
  end
4945
5091
  result = raw_result.to_ld(new_scale, rounding_mode)
5092
+ # puts "sum=#{sum} args.size=#{args.size} raw_result=#{raw_result} result=#{result} new_scale=#{new_scale} rounding_mode=#{rounding_mode}"
5093
+ return result
5094
+ end
5095
+
5096
+ # arithmetic mean with LongDecimalQuot as result (experimental)
5097
+ # parameters arguments for which arithmetic mean is calculated
5098
+ # result is exact if parameters are Integer, LongDecimal, LongDecimalQuot or Rational
5099
+ def LongMath.arithmetic_mean_ldq(*args)
5100
+ if (args.empty?)
5101
+ raise ArgumentError, "cannot calculate average of empty array"
5102
+ end
5103
+ sum = args.inject(LongDecimalQuot(0, 0)) do |psum,x|
5104
+ psum + x
5105
+ end
5106
+ result = sum / args.size
4946
5107
  return result
4947
5108
  end
4948
5109
 
@@ -4959,7 +5120,7 @@ module LongMath
4959
5120
  if (has_zero)
4960
5121
  return LongDecimal.zero!(new_scale)
4961
5122
  end
4962
- prod = args.inject(1) do |pprod,x|
5123
+ prod = args.inject(LongDecimal.one!(new_scale + 20)) do |pprod, x|
4963
5124
  pprod * x.abs
4964
5125
  end
4965
5126
  result = nil
@@ -4988,11 +5149,11 @@ module LongMath
4988
5149
  if (has_zero)
4989
5150
  raise ArgumentError, "cannot calculate harmonic mean of argument list containing zero #{args.inspect}"
4990
5151
  end
4991
- sum = args.inject(0) do |psum, x|
5152
+ sum = args.inject(LongDecimal.zero!) do |psum, x|
4992
5153
  psum + if (x.kind_of? Integer)
4993
5154
  Rational(1, x)
4994
5155
  else
4995
- 1/x
5156
+ LongDecimal.one!(new_scale+1)/x
4996
5157
  end
4997
5158
  end
4998
5159
  raw_result = args.size / sum
@@ -5000,6 +5161,28 @@ module LongMath
5000
5161
  return result
5001
5162
  end
5002
5163
 
5164
+ # harmonic mean with LongDecimalQuot as result (experimental)
5165
+ # parameters arguments for which arithmetic mean is calculated
5166
+ # result is exact if parameters are Integer, LongDecimal, LongDecimalQuot or Rational
5167
+ def LongMath.harmonic_mean_ldq(*args)
5168
+ result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(true, *args)
5169
+ if (all_same)
5170
+ return LongDecimalQuot(args)
5171
+ end
5172
+ if (has_zero)
5173
+ raise ArgumentError, "cannot calculate harmonic mean of argument list containing zero #{args.inspect}"
5174
+ end
5175
+ sum = args.inject(LongDecimalQuot(0, 0)) do |psum, x|
5176
+ psum + if (x.kind_of? Integer)
5177
+ Rational(1, x)
5178
+ else
5179
+ 1 / x
5180
+ end
5181
+ end
5182
+ result = args.size / sum
5183
+ return result
5184
+ end
5185
+
5003
5186
  # arithmetic-geometric mean (AGM)
5004
5187
  def LongMath.arithmetic_geometric_mean(new_scale, rounding_mode, *args)
5005
5188
  result_sign, all_same, has_neg, has_zero, has_pos = sign_check_for_mean(true, *args)
@@ -5044,7 +5227,10 @@ module LongMath
5044
5227
  if (all_same)
5045
5228
  return args[0].to_ld(new_scale, rounding_mode)
5046
5229
  end
5047
- sum = args.inject(0) do |psum, x|
5230
+ sum = args.inject(LongDecimal.zero!) do |psum, x|
5231
+ if (! (x.kind_of? LongDecimalBase) && ! (x.kind_of? Rational))
5232
+ x = x.to_ld(2*new_scale + 20)
5233
+ end
5048
5234
  psum + x*x
5049
5235
  end
5050
5236
  quot = if (sum.kind_of? Integer)
@@ -5065,11 +5251,14 @@ module LongMath
5065
5251
  if (all_same)
5066
5252
  return args[0].to_ld(new_scale, rounding_mode)
5067
5253
  end
5068
- sum = args.inject(0) do |psum, x|
5254
+ sum = args.inject(LongDecimal.zero!) do |psum, x|
5069
5255
  if (x.kind_of? Complex)
5070
5256
  raise ArgumentError, "cubic mean not supported for complex numbers args=#{args.inspect}"
5071
5257
  end
5072
- psum + x*x*x
5258
+ if (! (x.kind_of? LongDecimalBase) && ! (x.kind_of? Rational))
5259
+ x = x.to_ld(2*new_scale + 20)
5260
+ end
5261
+ psum + x ** 3
5073
5262
  end
5074
5263
  quot = if (sum.kind_of? Integer)
5075
5264
  Rational(sum, args.size)
@@ -5107,6 +5296,7 @@ module LongMath
5107
5296
 
5108
5297
  # round elements in such a way that round(new_scale, rounding_mode_sum, sum(elements)) = sum(elements_rounded)
5109
5298
  # HAARE_NIEMEYER
5299
+ # (experimental)
5110
5300
  def LongMath.round_sum_hm(new_scale, rounding_mode_sum, *elements)
5111
5301
  if (elements.empty?)
5112
5302
  return elements
@@ -5136,6 +5326,7 @@ module LongMath
5136
5326
 
5137
5327
  # round elements in such a way that round(new_scale, rounding_mode, sum(elements)) = sum(elements_rounded)
5138
5328
  # where rounding_mode_set is
5329
+ # (experimental)
5139
5330
  def LongMath.round_sum_divisor(new_scale, rounding_mode_sum, rounding_mode_set, *elements)
5140
5331
  if (elements.empty?)
5141
5332
  return elements