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.
@@ -4,8 +4,8 @@
4
4
  #
5
5
  # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
6
6
  #
7
- # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/test/testlongdeclib.rb,v 1.41 2011/01/16 21:12:42 bk1 Exp $
8
- # CVS-Label: $Name: RELEASE_1_00_00 $
7
+ # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/test/testlongdeclib.rb,v 1.42 2011/02/03 00:22:39 bk1 Exp $
8
+ # CVS-Label: $Name: $
9
9
  # Author: $Author: bk1 $ (Karl Brodowsky)
10
10
  #
11
11
 
@@ -21,13 +21,12 @@ class Integer
21
21
  end
22
22
  end
23
23
 
24
-
25
24
  #
26
25
  # test class for LongDecimal and LongDecimalQuot
27
26
  #
28
27
  module TestLongDecHelper
29
28
 
30
- @RCS_ID='-$Id: testlongdeclib.rb,v 1.41 2011/01/16 21:12:42 bk1 Exp $-'
29
+ @RCS_ID='-$Id: testlongdeclib.rb,v 1.42 2011/02/03 00:22:39 bk1 Exp $-'
31
30
 
32
31
  def assert_equal_float(lhs, rhs, delta=0, msg="")
33
32
  if ((lhs - rhs).abs >= delta)
@@ -37,11 +36,13 @@ module TestLongDecHelper
37
36
  end
38
37
 
39
38
  def assert_equal_rbo(lhs, rhs, msg="", lhsname="lhs", rhsname="rhs", delta=0)
40
- msg2 = "#{lhsname}=#{lhs} #{rhsname}=#{rhs} " + msg
39
+ msg2 = "#{lhsname}=#{lhs} (#{lhs.class}) #{rhsname}=#{rhs} (#{rhs.class}) " + msg
41
40
  if (lhs.kind_of? Rational) && (rhs.kind_of? BigDecimal) || (lhs.kind_of? BigDecimal) && (rhs.kind_of? Rational)
42
41
  assert_equal(lhs.to_ld, rhs.to_ld, msg2)
43
42
  elsif (delta > 0 && ((lhs.kind_of? Float) || (rhs.kind_of? Float)))
44
43
  assert_equal_float(lhs, rhs, delta, msg2 + " d=#{delta}")
44
+ elsif ((lhs.kind_of? Rational) && (rhs.kind_of? Rational))
45
+ assert_equal(lhs.numerator*rhs.denominator, lhs.denominator*rhs.numerator, msg)
45
46
  else
46
47
  assert_equal(lhs, rhs, msg2)
47
48
  end
@@ -499,6 +500,11 @@ module TestLongDecHelper
499
500
  y = y.to_ld
500
501
  # calculate z = x**y
501
502
  z = LongMath.power(x, y, prec)
503
+ prec_dp = 2*prec+1
504
+ z_dp = LongMath.power(x, y, prec_dp)
505
+ msg = "x=#{x}\ny=#{y}\nz=#{z}\nz_dp=#{z_dp}\nprec=#{prec}"
506
+ puts msg
507
+ assert((z - z_dp).abs <= 2*z.unit, msg)
502
508
 
503
509
  corr2 = (x - 1).abs*1000000000 # 10**9
504
510
  if (z.abs < LongMath::MAX_FLOATABLE && corr2 > 1)
@@ -509,23 +515,23 @@ module TestLongDecHelper
509
515
  zf = z.to_f
510
516
  qf = 1e9
511
517
  delta = [ z.unit.to_f, zf.abs / qf ].max
512
- # puts "delta=#{delta} z=#{z} zu=#{z.unit} zuf=#{z.unit.to_f} zf=#{zf} |zf/qf|=#{zf.abs/qf}"
513
518
  if (yf.abs > 1)
514
519
  l = Math.log(yf.abs)
515
520
  if (l > 1)
516
521
  delta *= l
517
522
  end
518
- # puts "delta=#{delta} l=#{l}"
519
523
  end
520
524
  corr = corr2 * 0.5
521
525
  if corr > 1
522
526
  corr_f = [ corr.to_f, 5.0 ].min
523
527
  delta *= corr_f
524
528
  end
525
- # puts "delta=#{delta} corr_f=#{corr_f} corr=#{corr}"
526
529
 
527
530
  diff = (zf - wf).abs
528
- assert_equal_float(zf, wf, delta, "z=#{z}=#{zf} and wf=#{wf.to_s} should be almost equal x=#{x}=#{xf} y=#{y}=#{yf} delta=#{delta} l=#{l} diff=#{diff} prec=#{prec} corr=#{corr}=#{corr.to_f} corr2=#{corr2}=#{corr2.to_f} corr_f=#{corr_f}")
531
+ msg = "z=#{z}=#{zf} and wf=#{wf.to_s} should be almost equal\nx=#{x}=#{xf}\ny=#{y}=#{yf}\ndelta=#{delta}\nl=#{l}\ndiff=#{diff}\nprec=#{prec}\ncorr=#{corr}=#{corr.to_f}\ncorr2=#{corr2}=#{corr2.to_f}\ncorr_f=#{corr_f}"
532
+ puts msg
533
+ assert_equal_float(zf, wf, delta, msg)
534
+ puts "OK"
529
535
  end
530
536
 
531
537
  # check by taking log(z) = y * log(x)
@@ -549,9 +555,14 @@ module TestLongDecHelper
549
555
  if (y.abs > 1) then
550
556
  l10y = (Math.log(y.abs.to_f) / Math.log(10)).ceil
551
557
  end
558
+ lprec_dp = 2*lprec+1
552
559
  u = LongMath.log(z, lprec)
560
+ u_dp = LongMath.log(z_dp, lprec_dp)
553
561
  v = LongMath.log(x, lprec+l10y)
562
+ v_dp = LongMath.log(x, lprec_dp + l10y)
554
563
  yv = (y*v).round_to_scale(lprec, LongDecimal::ROUND_HALF_DOWN)
564
+ yv_dp = (y * v_dp).round_to_scale(lprec_dp, LongDecimal::ROUND_HALF_DOWN)
565
+ assert((u_dp - yv_dp).abs <= unit, "u=log(z,#{lprec})=#{u_dp}=#{u} and yv=y*v=y*log(x,#{lprec+l10y})=#{yv_dp}=#{yv} should be almost equal (unit=#{unit} x=#{x.to_s} y=#{y.to_s} z=#{z_dp}=#{z.to_s} u=#{u_dp}=#{u.to_s} v=#{v_dp}=#{v.to_s} lprec=#{lprec} prec=#{prec} lprec_dp=#{lprec_dp} prec_dp=#{prec_dp})")
555
566
  assert((u - yv).abs <= unit, "u=log(z,#{lprec})=#{u} and yv=y*v=y*log(x,#{lprec+l10y})=#{yv} should be almost equal (unit=#{unit} x=#{x.to_s} y=#{y.to_s} z=#{z.to_s} u=#{u.to_s} v=#{v.to_s} lprec=#{lprec} prec=#{prec})")
556
567
  end
557
568
 
@@ -812,7 +823,6 @@ module TestLongDecHelper
812
823
  def check_cbrtb_with_remainder(x, s)
813
824
  y, r = LongMath.cbrtb_with_remainder(x)
814
825
  z0 = y.cube
815
- # puts "x=#{x} y=#{y} z0=#{z0} r=#{r}"
816
826
  z1 = z0 + r
817
827
  z2 = (y+1).cube
818
828
  assert(0 <= y, "cbrt _with_remainder must be >= 0" + s)
@@ -4,20 +4,21 @@
4
4
  #
5
5
  # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
6
6
  #
7
- # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/test/testrandlib.rb,v 1.9 2009/04/15 19:29:38 bk1 Exp $
8
- # CVS-Label: $Name: RELEASE_1_00_00 $
7
+ # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/test/testrandlib.rb,v 1.10 2011/02/03 00:22:39 bk1 Exp $
8
+ # CVS-Label: $Name: $
9
9
  # Author: $Author: bk1 $ (Karl Brodowsky)
10
10
  #
11
11
 
12
- #require "rubygems"
13
- require "crypt/ISAAC"
12
+ require "rubygems"
13
+ # require "crypt/ISAAC"
14
+ require "crypt-isaac"
14
15
 
15
16
  #
16
17
  # test class for LongDecimal and LongDecimalQuot
17
18
  #
18
19
  module TestRandomHelper
19
20
 
20
- @RCS_ID='-$Id: testrandlib.rb,v 1.9 2009/04/15 19:29:38 bk1 Exp $-'
21
+ @RCS_ID='-$Id: testrandlib.rb,v 1.10 2011/02/03 00:22:39 bk1 Exp $-'
21
22
 
22
23
  @@r1 = Crypt::ISAAC.new
23
24
  @@r2 = Crypt::ISAAC.new
@@ -4,17 +4,24 @@
4
4
  #
5
5
  # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
6
6
  #
7
- # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/test/testrandom.rb,v 1.17 2009/05/09 15:37:00 bk1 Exp $
8
- # CVS-Label: $Name: RELEASE_1_00_00 $
7
+ # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/test/testrandom.rb,v 1.18 2011/02/03 00:22:39 bk1 Exp $
8
+ # CVS-Label: $Name: $
9
9
  # Author: $Author: bk1 $ (Karl Brodowsky)
10
10
  #
11
11
 
12
- require "runit/testcase"
13
- require "runit/cui/testrunner"
14
- require "runit/testsuite"
12
+ $test_type = nil
13
+ if ((RUBY_VERSION.match /^1\./) || (RUBY_VERSION.match /^2\.0/)) then
14
+ require 'test/unit'
15
+ $test_type = :v20
16
+ else
17
+ require 'minitest/autorun'
18
+ require 'test/unit/assertions'
19
+ include Test::Unit::Assertions
20
+ $test_type = :v21
21
+ end
15
22
 
16
- #require "rubygems"
17
- require "crypt/ISAAC"
23
+ require "rubygems"
24
+ require "crypt-isaac"
18
25
 
19
26
  load "lib/long-decimal.rb"
20
27
  load "test/testlongdeclib.rb"
@@ -22,14 +29,23 @@ load "test/testrandlib.rb"
22
29
 
23
30
  LongMath.prec_overflow_handling = :warn_use_max
24
31
 
32
+ if ($test_type == :v20)
33
+ class UnitTest < Test::Unit::TestCase
34
+ end
35
+ else
36
+ class UnitTest < MiniTest::Test
37
+ end
38
+ end
39
+
25
40
  #
26
41
  # test class for LongDecimal and LongDecimalQuot
27
42
  #
28
- class TestRandom_class < RUNIT::TestCase
43
+ class TestRandom_class < UnitTest
29
44
  include TestLongDecHelper
30
45
  include TestRandomHelper
46
+ include LongDecimalRoundingMode
31
47
 
32
- @RCS_ID='-$Id: testrandom.rb,v 1.17 2009/05/09 15:37:00 bk1 Exp $-'
48
+ @RCS_ID='-$Id: testrandom.rb,v 1.18 2011/02/03 00:22:39 bk1 Exp $-'
33
49
 
34
50
  # for how many seconds should this test run? change to different
35
51
  # value on demand
@@ -40,14 +56,14 @@ class TestRandom_class < RUNIT::TestCase
40
56
  @scnt += 1
41
57
  puts("\ncnt=#{cnt} scnt=#{@scnt} x=#{x} ep=#{eprec} lp=#{lprec} sp=#{sprec} pp=#{pprec}\n")
42
58
  if (x <= LongMath::MAX_EXP_ABLE) then
43
- check_exp_floated(x, eprec)
59
+ check_exp_floated(x, eprec)
44
60
  end
45
61
  if (x > 0)
46
- check_log_floated(x, lprec)
62
+ check_log_floated(x, lprec)
47
63
  end
48
64
  if (x > 0)
49
- xr = x.round_to_scale(sc, LongMath::ROUND_HALF_UP)
50
- check_sqrt_with_remainder(xr, sprec, "x=#{x} p=#{sprec}")
65
+ xr = x.round_to_scale(sc, LongMath::ROUND_HALF_UP)
66
+ check_sqrt_with_remainder(xr, sprec, "x=#{x} p=#{sprec}")
51
67
  end
52
68
  end
53
69
  end
@@ -71,6 +87,6 @@ class TestRandom_class < RUNIT::TestCase
71
87
 
72
88
  end
73
89
 
74
- RUNIT::CUI::TestRunner.run(TestRandom_class.suite)
90
+ # RUNIT::CUI::TestRunner.run(TestRandom_class.suite)
75
91
 
76
92
  # end of file testrandom.rb
@@ -5,14 +5,20 @@
5
5
  # (C) Karl Brodowsky (IT Sky Consulting GmbH) 2006-2009
6
6
  #
7
7
  # CVS-ID: $Header: /var/cvs/long-decimal/long-decimal/test/testrandpower.rb,v 1.14 2009/05/09 15:37:00 bk1 Exp $
8
- # CVS-Label: $Name: RELEASE_1_00_00 $
8
+ # CVS-Label: $Name: $
9
9
  # Author: $Author: bk1 $ (Karl Brodowsky)
10
10
  #
11
11
 
12
- require "runit/testcase"
13
- require "runit/cui/testrunner"
14
- require "runit/testsuite"
15
- require "crypt/ISAAC"
12
+ $test_type = nil
13
+ if ((RUBY_VERSION.match /^1\./) || (RUBY_VERSION.match /^2\.0/)) then
14
+ require 'test/unit'
15
+ $test_type = :v20
16
+ else
17
+ require 'minitest/autorun'
18
+ require 'test/unit/assertions'
19
+ include Test::Unit::Assertions
20
+ $test_type = :v21
21
+ end
16
22
 
17
23
  load "lib/long-decimal.rb"
18
24
  load "lib/long-decimal-extra.rb"
@@ -22,12 +28,21 @@ load "test/testrandlib.rb"
22
28
 
23
29
  LongMath.prec_overflow_handling = :warn_use_max
24
30
 
31
+ if ($test_type == :v20)
32
+ class UnitTest < Test::Unit::TestCase
33
+ end
34
+ else
35
+ class UnitTest < MiniTest::Test
36
+ end
37
+ end
38
+
25
39
  #
26
40
  # test class for LongDecimal and LongDecimalQuot
27
41
  #
28
- class TestRandomPower_class < RUNIT::TestCase
42
+ class TestRandomPower_class < UnitTest
29
43
  include TestLongDecHelper
30
44
  include TestRandomHelper
45
+ include LongDecimalRoundingMode
31
46
 
32
47
  @RCS_ID='-$Id: testrandpower.rb,v 1.14 2009/05/09 15:37:00 bk1 Exp $-'
33
48
 
@@ -72,6 +87,6 @@ class TestRandomPower_class < RUNIT::TestCase
72
87
 
73
88
  end
74
89
 
75
- RUNIT::CUI::TestRunner.run(TestRandomPower_class.suite)
90
+ # RUNIT::CUI::TestRunner.run(TestRandomPower_class.suite)
76
91
 
77
92
  # end of file testrandpower.rb
Binary file
@@ -0,0 +1,420 @@
1
+ % LaTeX
2
+ % $Header: /var/cvs/long-decimal/long-decimal/tex/long-decimal.tex,v 1.3 2011/02/09 22:51:52 bk1 Exp $
3
+ % $Name: $
4
+ %
5
+ \documentclass[10pt,a4paper]{article}
6
+ \usepackage{isodate}
7
+ \usepackage[T1]{fontenc}
8
+ \usepackage[latin1]{inputenc}
9
+ \usepackage{graphics}
10
+ \usepackage{amsmath,amssymb}
11
+ \usepackage[a4paper,margin=3cm,footskip=.5cm]{geometry}
12
+
13
+ \title{The Ruby library {\slshape long-decimal}}
14
+ \author{Karl Brodowsky\\
15
+ IT Sky Consulting GmbH\\
16
+ Switzerland}
17
+
18
+ \date{2011-02-06}
19
+
20
+ %\makeatletter
21
+ %\makeatother
22
+
23
+ \setlength{\topmargin}{0mm}
24
+ %\setlength{\leftmargin}{10mm}
25
+ \setlength{\oddsidemargin}{-11mm}
26
+ \setlength{\evensidemargin}{-11mm}
27
+ \setlength{\parindent}{0mm}
28
+
29
+ %\newfont{\ocrb}{ocrb10}
30
+
31
+ \def\ld{\mathrm l}
32
+ \def\ldq{\mathrm q}
33
+
34
+ \begin{document}
35
+ \sffamily
36
+ \maketitle
37
+
38
+ \begin{abstract}
39
+
40
+ The goal of the Ruby-library {\slshape long-decimal}\/ is to provide a new
41
+ numerical type {\slshape LongDecimal\/}\/ and arithmetic operations to deal
42
+ with this type.
43
+
44
+ \end{abstract}
45
+
46
+ \section{Introduction}
47
+
48
+ Ruby supports non-integral numbers with the built-in types {\slshape
49
+ Float\/} and {\slshape Rational\/} and {\slshape BigDecimal\/}.
50
+ While these are very useful for many purposes, the development of
51
+ finance application requires the availability of a type like
52
+ {\slshape LongDecimal\/}, that allows explicit control of the rounding and uses a
53
+ decimal representation for the fractional part. An instance of
54
+ {\slshape LongDecimal\/} can be represented as a pair of integral
55
+ numbers $(n, d)$, where the value of the number is $\frac{n}{10^d}$.
56
+ The ring-operations $+$, $-$ and $*$ can be trivially defined in a way
57
+ that does not loose any information (and thus does not require any
58
+ rounding).
59
+
60
+ Division requires some additional thought, because the exact quotient
61
+ of two {\slshape LongDecimal\/}s can very well be expressed as
62
+ Rational, but not always as {\slshape LongDecimal\/}. A supplementary
63
+ numeric type {\slshape LongDecimalQuot\/} has been introduced to
64
+ support storing pairs $(r, d)$, where $r$ is a rational number and $d$
65
+ is an estimation about the significant digits after the decimal point.
66
+ The represented value is the rational number $r$.
67
+
68
+ Calculation of roots and trancendental functions require additional
69
+ information as input parameters to control the number of digits after
70
+ the decimal point and the rounding rules for achieving the result.
71
+
72
+ In the remainder of this document we will write $\ld(n, d)$ for the
73
+ {\slshape LongDecimal\/}-number represented by the pair $(n, d)$ in
74
+ the manner described above and $\ldq(r, d)$ for the {\slshape
75
+ LongDecimalQuot\/}-number represented by the pair $(r, n)$. It
76
+ needs to be observed that the part $d$ in both cases carries
77
+ information about the precision of the number in terms of significant
78
+ digits after the decimal point.
79
+
80
+ \pagebreak
81
+
82
+ \section{Ring Operations}
83
+
84
+ The ring operations $+$, $-$ and $*$ and obvious derived operations
85
+ like negation are defined like this:
86
+
87
+ $$\bigwedge_{e,d \in \mathbb{N}_0} \bigwedge_{m, n \in \mathbb{Z}}
88
+ \ld(m,d) + \ld(n,e) = \ld(m \cdot 10^{\max(d, e)-d} + n \cdot 10^{\max(d, e)-e}, \max(d, e))$$
89
+ $$\bigwedge_{e,d \in \mathbb{N}_0} \bigwedge_{m, n \in \mathbb{Z}}
90
+ \ld(m,d) - \ld(n,e) = \ld(m \cdot 10^{\max(d, e)-d} - n \cdot 10^{\max(d, e)-e}, \max(d, e))$$
91
+ $$\bigwedge_{e,d \in \mathbb{N}_0} \bigwedge_{m, n \in \mathbb{Z}}
92
+ \ld(m,d) \cdot \ld(n,e) = \ld(m n, d+e)$$
93
+
94
+ \pagebreak
95
+
96
+ \section{Rounding Operations}
97
+
98
+ The library supports two rounding operations that can be applied both
99
+ to {\slshape LongDecimal\/} and {\slshape LongDecimalQuot\/}. In the
100
+ latter case it implies a conversion to {\slshape LongDecimal\/}. The
101
+ normal rounding function {\slshape round\_to\_scale\/} changes the
102
+ precision to the given value, while keeping the value expressed by the
103
+ number approximately the same.
104
+
105
+ The optional second parameter describes how rounding will be done, if the
106
+ process looses information. It defaults to {\slshape
107
+ ROUND\_UNNECESSARY\/}, in which case an exception is raised whenever
108
+ a value changing rounding process would be required.
109
+
110
+ The other rounding operations can be grouped into two groups:
111
+ ROUND\_UP, ROUND\_DOWN, ROUND\_CEILING and ROUND\_FLOOR already answer
112
+ the question of how to round completely because they use the values
113
+ that result from rounding as boundaries and obviously round these to
114
+ themselves.
115
+
116
+ The other rounding operations put a boundary somewhere in the middle
117
+ between allowed rounded values, but they leave the question of how to
118
+ round the boundary itself to be answered as well. This is expressed
119
+ by having a major rounding mode that defines where the boundaries lie
120
+ and a minor rounding mode that is applicable for values that lie
121
+ exactly on the boundary. In some cases this cannot happen, because
122
+ boundaries are irrational and {\slshape LongDecimal\/} (and {\slshape LongDecimalQuot\/}) can
123
+ only express (some) rational numbers, but for the sake of completeness
124
+ and applicability to other representations, that might allow some
125
+ irrational values, this is done in a uniform way. Since rounding
126
+ operations can also apply to internal intermediate results, it is a
127
+ good idea not to constrain their definition to rational numbers.
128
+
129
+ Major rounding modes are
130
+
131
+ \begin{tabular}{|l|p{100mm}|}
132
+ \hline
133
+ MAJOR\_UP & always round in such a way that the absolute value does not decrease.No minor rounding mode needed.\\
134
+ \hline
135
+ MAJOR\_DOWN & always round in such a way that the absolute value does not increase. No minor rounding mode needed.\\
136
+ \hline
137
+ MAJOR\_CEILING & always round in such a way that the rational value does not decrease. No minor rounding mode needed.\\
138
+ \hline
139
+ MAJOR\_FLOOR & always round in such a way that the rational value does not increase. No minor rounding mode needed.\\
140
+ \hline
141
+ MAJOR\_HALF & round to the nearest available value. The boundary is the arithmetic mean of the two adjacent available values.\\
142
+ \hline
143
+ MAJOR\_GEOMETRIC & round to one of the two adjacent available values $x,y$ and use their geometric mean $\sqrt{xy}$ as boundary. For negative $x,y$ use the negated square root instead.\\
144
+ \hline
145
+ MAJOR\_HARMONIC & round to one of the two adjacent available values $x,y$ and use their harmonic mean $\frac{2xy}{x+y}$ as boundary.\\
146
+ \hline
147
+ MAJOR\_QUADRATIC & round to one of the two adjacent available values $x,y$ and use their quadratic mean $\sqrt{\frac{x^2+y^2}{2}}$ as boundary. If $x,y\le 0$ use the negated square root instead.\\
148
+ \hline
149
+ MAJOR\_CUBIC & round to one of the two adjacent available values $x,y$ and use their cubic mean $\sqrt[3]{\frac{x^3+y^3}{2}}$ as boundary.\\
150
+ \hline
151
+ MAJOR\_UNNECESSARY & raise an exception if value changing rounding would be needed\\
152
+ \hline
153
+ \end{tabular}
154
+
155
+ \pagebreak
156
+
157
+ Minor rounding modes are
158
+
159
+
160
+ \begin{tabular}{|l|p{100mm}|}
161
+ \hline
162
+ {\bfseries rounding mode}&{\bfseries description}\\
163
+ \hline
164
+ MINOR\_UNUSED & no minor rounding mode applies, can only be combined with ROUND\_UP, ROUND\_DOWN, ROUND\_CEILING and ROUND\_FLOOR.\\
165
+ \hline
166
+ MINOR\_UP & round values exactly on the boundary by increasing the absolute value (away from zero).\\
167
+ \hline
168
+ MINOR\_DOWN & round values exactly on the boundary by decreasing the absolute value (towards zero).\\
169
+ \hline
170
+ MINOR\_CEILING & round values exactly on the boundary by increasing the rational value (towards $\infty$).\\
171
+ \hline
172
+ MINOR\_FLOOR & round values exactly on the boundary by decreasing the rational value (towards $-\infty$).\\
173
+ \hline
174
+ MINOR\_EVEN & round values exactly on the boundary by using the choice resulting in the rounded last digit to be even.\\
175
+ \hline
176
+ MINOR\_ODD & round values exactly on the boundary by using the choice resulting in the rounded last digit to be odd.\\
177
+ \hline
178
+ \end{tabular}
179
+
180
+ \pagebreak
181
+
182
+ These combine to rounding modes:
183
+
184
+ \begin{tabular}{|l|p{100mm}|}
185
+ \hline
186
+ {\bfseries rounding mode}&{\bfseries description}\\
187
+ \hline
188
+ ROUND\_UP & always round in such a way that the absolute value does not decrease.\\
189
+ \hline
190
+ ROUND\_DOWN & always round in such a way that the absolute value does not increase.\\
191
+ \hline
192
+ ROUND\_CEILING & always round in such a way that the rational value does not decrease.\\
193
+ \hline
194
+ ROUND\_FLOOR & always round in such a way that the rational value does not increase.\\
195
+ \hline
196
+ ROUND\_HALF\_UP & round to the nearest available value, prefer increasing the absolute value if the last digit is 5\\
197
+ \hline
198
+ ROUND\_HALF\_DOWN & round to the nearest available value, prefer decreasing the absolute value if the last digit is 5\\
199
+ \hline
200
+ ROUND\_HALF\_CEILING & round to the nearest available value, prefer increasing the rational value if the last digit is 5\\
201
+ \hline
202
+ ROUND\_HALF\_FLOOR & round to the nearest available value, prefer decreasing the rational value if the last digit is 5\\
203
+ \hline
204
+ ROUND\_HALF\_EVEN & round to the nearest available value, prefer the resulting last digit to be even if the last digit prior to rounding is 5\\
205
+ \hline
206
+ ROUND\_HALF\_ODD & round to the nearest available value, prefer the resulting last digit to be odd if the last digit prior to rounding is 5\\
207
+ \hline
208
+ ROUND\_GEOMETRIC\_UP & round to the available value using the geometric mean as boundary, prefer increasing the absolute value if the unrounded value is exactly on the boundary\\
209
+ \hline
210
+ ROUND\_GEOMETRIC\_DOWN & round to the available value, using the geometric mean as boundary, prefer decreasing the absolute value if the unrounded value is exactly on the boundary\\
211
+ \hline
212
+ \dots & \ldots\\
213
+ \hline
214
+ ROUND\_HARMONIC\_UP & round to the available value using the harmonic mean as boundary, prefer increasing the absolute value if the unrounded value is exactly on the boundary\\
215
+ \hline
216
+ ROUND\_HARMONIC\_DOWN & round to the available value, using the harmonic mean as boundary, prefer decreasing the absolute value if the unrounded value is exactly on the boundary\\
217
+ \hline
218
+ \dots & \ldots\\
219
+ \hline
220
+ ROUND\_QUADRATIC\_UP & round to the available value using the quadratic mean as boundary, prefer increasing the absolute value if the unrounded value is exactly on the boundary\\
221
+ \hline
222
+ ROUND\_QUADRATIC\_DOWN & round to the available value, using the quadratic mean as boundary, prefer decreasing the absolute value if the unrounded value is exactly on the boundary\\
223
+ \hline
224
+ \dots & \ldots\\
225
+ \hline
226
+ ROUND\_CUBIC\_UP & round to the available value using the cubic mean as boundary, prefer increasing the absolute value if the unrounded value is exactly on the boundary\\
227
+ \hline
228
+ ROUND\_CUBIC\_DOWN & round to the available value, using the cubic mean as boundary, prefer decreasing the absolute value if the unrounded value is exactly on the boundary\\
229
+ \hline
230
+ \dots & \ldots\\
231
+ \hline
232
+ ROUND\_CUBIC\_ODD & round to the available value using the cubic mean as boundary, prefer the resulting last digit to be odd if the unrounded value is exactly on the boundary\\
233
+ \hline
234
+ ROUND\_UNNECESSARY & raise an exception if value changing rounding would be needed\\
235
+ \hline
236
+ \end{tabular}
237
+
238
+
239
+ In addition to these commonly available rounding operations {\slshape long-decimal\/} provides rounding to remainder sets. This is motivated by the practice in some
240
+ currencies to prefer using certain multiples of the smallest unit. For example in CHF you commonly use two digits after the decimal point, but the last digit is
241
+ required to be 0 or 5. This is achieved as a special case of a more general concept {\slshape round\_to\_allowed\_remainders\/}. A modulus $M \ge 2$ and a set
242
+ $R\subset\mathbb{N}_0$ are given and a number $x$ is rounded to $\ld(n, d)$ such that
243
+
244
+ $$\bigvee_{r\in R} n \equiv r \mod M$$
245
+
246
+ Typically $M$ is ten or a power of ten, but this is not required. Neither is it required that zero is a member of $R$. In that case an additional parameter is needed
247
+ in order to define to which direction a potential last digit of zero would be rounded. In the CHF case we would have $M=10$ and $R=\{0, 5\}$.
248
+
249
+ This kind of rounding can be applied to integers as well as to
250
+ {\slshape LongDecimal\/} and {\slshape LongDecimalQuot\/}, in which case the integral numerator
251
+ $n$ of $\ld(n, d)=\frac{n}{10^d}$ must fullfill the additional
252
+ constraint. In order to avoid complications, this is not supported in
253
+ conjunction with minor rounding modes MINOR\_EVEN and MINOR\_ODD,
254
+ because all matching rounded values might be even or odd and we cannot
255
+ rely on the alternation of even and odd values.
256
+ It is possible to exclude $0$ from the set $R$. In this case a
257
+ ZERO-rounding-mode needs to be provided that tells in which way a
258
+ zero, should it occur, shoud be rounded.
259
+
260
+ \pagebreak
261
+
262
+ \section{Division}
263
+
264
+ Regular division using $/$ yields an instance of {\slshape LongDecimalQuot\/}. The approximate number of significant digits is estimated by using the partial derivatives of $f(x, y) = \frac{x}{y}$.
265
+ Assume $x=\ld(m, s)$ and $y=\ld(n, t)$. So we get for $\ld(m, s) / \ld(n, t)$ an result
266
+
267
+ $$\frac{x}{y} = \ldq(10^{t-s}\frac{m}{n}, r)$$
268
+
269
+ with
270
+
271
+ $$r \approx -\log_{10}\left(10^{-s} \frac{1}{|y|} + 10^{-t} \frac{|x|}{y^2}\right)
272
+ = 2 \log_{10}(y) + s + t -\log_{10}\left(|m| + |n|) \right)$$
273
+
274
+ In order to avoid expensive logarithmic operation for such basic operations as division this is approximated by
275
+
276
+ $$r = \max( 0, 2 v + s + t - \max( u + s, v + t) - 3)$$
277
+ with $u = \lfloor\log |m| \rfloor - s + 1$ and $v = \lfloor\log |n| \rfloor - t + 1$ for $m$ and $n$ not zero
278
+ and $u=-s$ for $m = 0$ and $v=-t$ for $n=0$. Using $\max(a, b)$ instead of $log_{10}(10^a + 10^b)$ is a good approximation, when a and b are far apart.
279
+
280
+ It is recommended to use explicit rounding after having performed
281
+ division rather than to rely on this somewhat arbitrary estimation on
282
+ the number of significant digits. It is possible to retain
283
+ intermediate results with {\slshape LongDecimalQuot\/}, because the
284
+ full arithmetic is available for this type as well, but using rational
285
+ numbers internally it will blow up numerator and denominator to an
286
+ extent that diminishes performance by quite a margin in longer
287
+ calculations.
288
+
289
+ \pagebreak
290
+
291
+ \section{Roots}
292
+
293
+ Square root and cube root of {\slshape LongDecimal\/} can be calculated quite
294
+ efficiently using an algorithm that is somewhat similar to the
295
+ algorithm for long integer division. This has been preferred over the
296
+ more commonly known Newton algorithm. Since square and cube roots are
297
+ usually irrational, it is mandatory to provide rounding information
298
+ concerning the number of desired digits and the rounding mode.
299
+
300
+ Square roots and cube roots can also calculated of integers, in which
301
+ case a variant is available that also calculates a remainder $r$ in
302
+ addition to the approximated square root $s$, such
303
+ that
304
+ $a = s^2+r$.
305
+
306
+ \section{$\pi$}
307
+
308
+ The number $\pi$ can be calculated to a given number of digits, which
309
+ works sufficiently fast for a few thousend digits.
310
+
311
+ Please use dedicated programs if you seriously want to calculate $\pi$
312
+ to millions of digits, Ruby is not fast enough for this kind of number
313
+ crunching to compete with the best C-programs.
314
+
315
+ \pagebreak
316
+
317
+ \section{Transcendental Functions}
318
+
319
+ It is the goal of this library to support the most common
320
+ transcendental functions in the long run. Currently $\exp$ and $\log$
321
+ are supported. These are calculated using the Taylor series, but the
322
+ calculation has been improved. Most important it is to improve the
323
+ convergence, which is the case with a series of the form
324
+
325
+ $$\sum_{n=0}^\infty \frac{x^n}{n!}$$
326
+
327
+ for $x< 0.5$.
328
+
329
+ For the exponential function this can always be achieved by using the following transformations: $x = 2^n x_0$ with $x_0 < 0.5$ implies
330
+
331
+ $$\exp(x) = \exp(x_0)^{2^n}$$
332
+
333
+ which can be easily calculated using successive squaring operations.
334
+
335
+ For the logarithm we first use $x = e^n x_0$ with $n \in \mathbb{N}_0$ and $x_0 < e$ with
336
+
337
+ $$\log(x) = n + \log(x_0).$$
338
+
339
+ From here we make use of the efficient square root calculation facility and use
340
+
341
+ $$\bigwedge_{m=0}^\infty x_{m+1} = \sqrt{x_m}$$
342
+
343
+ and
344
+
345
+ $$\log(x) = n + 2^m \log x_m$$
346
+
347
+ Using this $x_m$ can be brought sufficiently close to $1$ to make the Taylor series of $\log$ converge sufficiently fast to be useful for the calculation:
348
+
349
+ $$\log(x_m) = \sum_{k=1}^\infty \frac{(x_m-1)^k(-1)^{k+1}}{k}$$
350
+
351
+ Another minor optimization uses some integer $j$ and adds
352
+
353
+ $$ \bigwedge_{l=0}^{j-1} s_k = \sum_{l=0}^n \frac{x^{lj}}{(lj+k)!}$$
354
+
355
+ from which we finally multiply the partial sums with appropriate low powers of $x$. This saves on the number of multiplications.
356
+ Similar patterns will be applied to the calculation of other transcendental functions.
357
+
358
+ \pagebreak
359
+
360
+ \section{Powers}
361
+
362
+ Calculation of powers with any positive {\slshape LongDecimal\/} as base and any {\slshape LongDecimal\/} as exponent is quite challenging to do.
363
+
364
+ It is quite trivial that $x^y = \exp(y \log x)$, but the challenges
365
+ are to achieve the result in acceptable time and with the required
366
+ precision. The builtin class {\slshape BigDecimal} does not meet
367
+ these goals, because it becomes incredibly slow for certain
368
+ combinations of $x$ and $y$. The power function of {\slshape LongDecimal\/} has
369
+ been optimized to handle a broad range of cases with different
370
+ approaches to provide precision and speed. This should work fine for
371
+ reasonable practical use, but it will still be possible to construct
372
+ corner cases with bases very close to 1 and large exponents which fail
373
+ in an attempt to do an accurate calculation in their last digits.
374
+
375
+ \section{Means}
376
+
377
+ In the class {\slshape LongMath} there are methods for calculating the
378
+ following means: arithmetic\_mean, geometric\_mean, harmonic\_mean,
379
+ quadratic\_mean, cubic\_mean, arithmetic\_geometric\_mean,
380
+ harmonic\_geometric\_mean. See Wikipedia for their definitions.
381
+
382
+ \section{Rounding with sum constraint}
383
+
384
+ Experimental support for rounding of several number simultanously in
385
+ such a way that their rounded sum is the sum of the rounded numbers is
386
+ included with the methods {\slshape LongMath.round\_sum\_hm} which uses
387
+ the Haare-Niemeyer-approach and {\slshape LongMath.round\_sum\_divisor}
388
+ which uses one of several divisor based approaches, like D'Hondt. The
389
+ approach is chosen by the rounding mode. These are not yet implemented
390
+ efficiently nor are they tested well, so use at your own risk (like
391
+ the whole library).
392
+
393
+ \section{Limitations}
394
+
395
+ Rounding with sum constraint is not yet production stable.
396
+
397
+ Powers with bases that are off 1 by $10^{-20}$ or less and exponents
398
+ in the order of magnitude of $10^20$ or more are not always calculated
399
+ precisely.
400
+
401
+ Some interesting transcendental functions are missing.
402
+
403
+ Many operations (like power, exp, log and other transcendental functions) fail to work with numbers whose magnitude cannot be
404
+ expressed as double.
405
+
406
+ Using transcendental functions that have a rounding mode and precision
407
+ as part of their parameter set has not been tested with the newer
408
+ rounding modes, \dots\_ODD, ROUND\_GEOMETRIC\_\dots,
409
+ ROUND\_HARMONIC\_\dots, ROUND\_QUADRATIC\_\dots and
410
+ ROUND\_CUBIC\_\dots.
411
+ These combinations will be tested and improved in future versions.
412
+
413
+ \section{Tests}
414
+
415
+ Unit tests have been added to test much of the implemented
416
+ functionality.
417
+
418
+ More unit tests are desirable.
419
+
420
+ \end{document}