long-decimal 1.00.01 → 1.00.02

Sign up to get free protection for your applications and to get access to all the features.
@@ -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}