33Equations, algorithms, and other tricks used to calculate the Time Value of
44Money (TVM) variables in the RPN83P calculator app.
55
6- ** Version** : 0.9.0 (2024-01-06 )
6+ ** Version** : 0.12.0-rc1 (2024-06-18 )
77
88** Project Home** : https://github.com/bxparks/rpn83p
99
@@ -20,7 +20,7 @@ Money (TVM) variables in the RPN83P calculator app.
2020 - [ Taming Overflows] ( #taming-overflows )
2121 - [ Secant Method] ( #secant-method )
2222 - [ Initial Guesses] ( #initial-guesses )
23- - [ Tolerance ] ( #tolerance )
23+ - [ Terminating Conditions ] ( #terminating-conditions )
2424 - [ Maximum Iterations] ( #maximum-iterations )
2525 - [ Convergence] ( #convergence )
2626- [ References] ( #references )
@@ -69,23 +69,55 @@ NPV = NFV * (1+i)^N
6969
7070## Interest Rate Conversions
7171
72- The variable ` i ` in the ` NPV ` and ` NFV ` equations defines the interest rate per
73- payment period. For most real-life problems, it is more convenient to express
74- the interest rate per year. The relationship between these quantities are:
72+ Most real-life problems express the nominal annual interest rate ` I%YR ` as a
73+ percentage instead of a dimensionless fraction ` IYR ` which is more useful in
74+ computation. So let's define:
75+
76+ ```
77+ IYR = I%YR / 100
78+ ```
79+
80+ The variable ` i ` in the ` NPV ` and ` NFV ` equations is the interest rate per
81+ payment period, which is defined from the nominal interest rate ` IYR ` by:
7582
7683```
7784i = IYR/PYR
7885```
7986
80- where:
87+ where ` PYR ` is the number of payments per year
88+
89+ However, this is true only if ` PYR ` is equal to ` CYR ` , the number of
90+ compoundings per year. It seems that in some countries (e.g. Canada and the UK),
91+ lenders are required to calculate the ` CYR ` differently from the ` PYR ` .
92+
93+ When the 2 quantities are different, the total effective annual interest rate
94+ that results from compounding for each payment period at interest rate ` i ` must
95+ equal the total effective annual interest rate when compounded using the ` CYR `
96+ compounds per year. In other words, the following defines the meaning of ` CYR ` :
97+
98+ ```
99+ (1 + i) ^ PYR = (1 + IYR/CYR) ^ CYR
100+ ```
101+
102+ We can rearrange the equation to solve the payment-period interest rate ` i `
103+ which is used on the TVM equations:
104+
105+ ```
106+ i = (1 + IYR/CYR) ^ (CYR/PYR) - 1
107+ = expm1[ (CYR/PYR) log1p(IYR/CYR) ]
108+ ```
109+
110+ where we have used the ` expm1() ` and ` log1p() ` functions that we defined below.
111+
112+ Since the ` expm1() ` and ` log1p() ` functions are inverses of each other, we can
113+ easily get the formula for calculating ` IYR ` from ` i ` :
114+
81115```
82- IYR = nominal interest rate per year (without compounding)
83- PYR = number of payments per year
116+ IYR = CYR expm1[ (PYR/CYR) log1p(i) ]
84117```
85118
86- In addition, most real-life problems express the annual interest rate as a
87- percentage instead of a dimensionless fraction. So ` IYR = I%YR / 100 ` , where
88- ` I%YR ` is the nominal interest percentage per year.
119+ From ` IYR ` , we can calculate the nominal percent interest rate ` I%YR ` as
120+ ` 100*IYR ` .
89121
90122## TVM Formulas
91123
@@ -361,6 +393,12 @@ equation are *unchanged*. Let's define a new term `CFN(i,N)`:
361393
362394```
363395CFN(i,N) = CF2(i)/N = [(1+i)^N-1]/Ni
396+
397+ expm1(N log1p(i)) / Ni (i!=0)
398+ /
399+ =
400+ \
401+ 1 (i==0)
364402```
365403
366404(Note that this is slightly different than the ` C(i,N) ` function defined by
@@ -374,8 +412,9 @@ called `NPMT(i)`:
374412
375413```
376414NPMT(i) = NFV(i) / CFN(i,N)
377- = PV (1+i)^N Ni / [(1+i)^N-1] + (1+ip)N*PMT + FV / CFN(i,N)
378- = PV (-N)i / [(1+i)^(-N)-1] + (1+ip)N*PMT + FV / CFN(i,N)
415+ = PV (1+i)^N Ni / [(1+i)^N-1] + (1+ip)N*PMT + FV Ni / [(1+i)^N-1]
416+ = PV (1+i)^N Ni / [(1+i)^N-1] + (1+ip)N*PMT + FV Ni / [(1+i)^N-1]
417+ = PV (-N)i / [(1+i)^(-N)-1] + (1+ip)N*PMT + FV Ni / [(1+i)^N-1]
379418 = PV / CFN(i,-N) + (1+ip)N*PMT + FV / CFN(i,N)
380419```
381420
@@ -432,11 +471,18 @@ beginning or end of the cash flow. The `NPMT(i)` function essentially averages
432471all 3 terms over the entire duration of the ` N ` payment periods, instead of
433472pulling everything to the present or pushing everything to the future.
434473
435- ** Implementation Note** : The ` inverseCompoundingFactor() ` routine calculates the
436- reciprocal of ` CFN(i,N) ` . In other words, it calculates `ICFN(i,N) = 1/CFN(i,N)
437- = Ni/((1+i)^N-1)` for a slight gain in efficiency by avoiding a division or two.
438- This makes ` ICFN(i,N) ` similar to the ` C(n) ` function given by Albert Chan in
439- [ TVM formula error in programming
474+ ** Implementation Note** :
475+
476+ The ` inverseCompoundingFactor() ` routine calculates the reciprocal of
477+ ` CFN(i,N) ` . In other words, it calculates
478+
479+ ```
480+ ICFN(i,N) = 1/CFN(i,N) = Ni/((1+i)^N-1)
481+ ```
482+
483+ for a slight gain in efficiency by avoiding a division or two. This makes
484+ ` ICFN(i,N) ` similar to the ` C(n) ` function given by Albert Chan in [ TVM formula
485+ error in programming
440486manual?] ( https://www.hpmuseum.org/forum/thread-20739-post-179371.html#pid179371 )
441487(2023), within a factor of ` (1+i)^N ` , or equivalently, a substitution of ` -N `
442488for ` N ` .
@@ -472,20 +518,31 @@ of about 1e-8 within 7-8 iterations.
472518### Initial Guesses
473519
474520The Secant method (as well as other root finding methods) requires 2 initial
475- guesses to be provided. The TVM Solver currently (v0.9.0) uses the following
476- defaults:
477-
478- - ` i0 ` = 0
479- - ` i1 ` = 1/PYR, where PYR is the number of payments per year.
480-
481- The value of ` i0 ` can be set to ` 0 ` because all of our various terms and
482- equations (` ICFN(i,N) ` , ` NPMT(i) ` ) have been defined to remove their singularity
483- at ` i=0 ` . Those functions are now continuous (and probably infinitely
484- differentiable) at ` i=0 ` .
485-
486- The value of ` i1 ` is actually specified as ` IYR1 ` of 100%, a nominal yearly rate
487- of 100%. Most real-life problems will never need to search for an interest rate
488- higher than 100%/year. The ` i1 ` variable is ` IYR/PYR ` or ` 100%/PYR ` or ` 1/PYR ` .
521+ guesses to be provided. The TVM Solver (v0.12.0) uses the following defaults:
522+
523+ - ` IYR1 ` = -50%
524+ - ` IYR2 ` = 100%
525+
526+ We calculate the internal payment-period initial guesses ` i1 ` and ` i2 ` using the
527+ formula ` i=expm1[(CYR/PYR) log1p(IYR/CYR)] ` as given above. From these starting
528+ points ` i1 ` and ` i2 ` , the TVM Solver makes a few more heuristic guesses:
529+
530+ - If the interval ` [i1, i2] ` overlaps the 0% point, the intervals are split into
531+ two on either side of the ` 0 ` :
532+ - ` [i1,0] ` and ` [0,i2] `
533+ - the positive interest interval ` [0,i2] ` is checked first, and if no sign
534+ change is detected, then,
535+ - the negative interest interval is checked
536+ - If no sign change is detected in either ` [i1,0] ` and ` [0,i2] ` , then we take
537+ the positive interval ` [0,i2] ` and decompose that into 2 intervals again:
538+ - check the lower interval ` [0,(i1+i2)/2] ` first, and if no sign change
539+ is found, then
540+ - check the upper interval ` [(i1+i2)/2, i2] `
541+
542+ The value of ` i=0 ` is allowed in the various candidate intervals because all of
543+ our various terms and equations (` ICFN(i,N) ` , ` NPMT(i) ` ) have been defined to
544+ remove their singularity at ` i=0 ` . Those functions are now continuous (and
545+ probably infinitely differentiable) at ` i=0 ` .
489546
490547The following posts by Albert Chan suggest that there are better initial guess:
491548
@@ -502,38 +559,46 @@ checks if the very first guesses bracket a root with a change in sign of the
502559` NPMT(i) ` function. If no sign change is detected, a ` TVM No Solution ` error
503560message is returned.
504561
505- ### Tolerance
562+ ### Terminating Conditions
506563
507- Currently (v0.9.0), the TVM Solver iterates until the relative tolerance between
508- two successive iterations is less than 1e-8. The value of 1e-8 is hardcoded.
564+ TVM Solver iterates until the relative tolerance between two successive
565+ iterations is less than some amount. The root finding iteration stops when one
566+ of the following conditions are met:
509567
510- To avoid division by zero, the tolerance is calculated as:
568+ - ` f0==0 `
569+ - ` f1==0 `
570+ - ` f1==f0 ` and ` iterationCount>=3 `
571+ - ` |i1-i0|<=tol*max(|i1|,|i0|) ` , ` tol=1e-10 `
511572
512- ```
513- |i1 - i0| / |i1|, if |i1|!=0
514- /
515- tol =
516- \
517- |i1 - i0|, if i1==0
573+ The values ` f0 ` and ` f1 ` are defined to be ` f0=NPMT(i0) ` and ` f1=NPMT(i1) ` and
574+ the conditions ` f0==0 ` and ` f1==0 ` detect the situation where an iteration lands
575+ directly on the root of the equation.
518576
519- ```
577+ The ` f1==f0 ` condition happens when the 2 successive iteration produces no
578+ change in the value of ` NPMT(i) ` . The ` iterationCount>=3 ` comes from avoiding
579+ the situation where the very early guesses just happened to evaluate to a secant
580+ line with zero slope, which results in a division-by-zero error when evaluating
581+ the next iteration.
520582
521- I am sure this is not optimal, but I needed a quick way to avoid a division by
522- zero if ` i1 ` was equal to zero.
583+ The complicated equation involving the ` tol ` parameter detects the convergence
584+ of successive iterations of ` i0 ` and ` i1 ` . The equation was selected to deal
585+ with situations where the solution converges to a value that is equal or very
586+ close to ` 0 ` .
523587
524- Maybe the tolerance limit (1e-8) should be exposed to the user and be
525- configurable? I believe the HP calculators use the number of significant digits
526- in its ` FIX ` mode to determine the tolerance limit. But TI calculator users tend
527- not to use ` FIX ` modes, leaving the calculator with the maximum number of
528- significant digits (i.e. the ` FIX(-) ` mode). I'm not sure that using the number
529- of digits from the ` FIX ` mode is a reasonable mechanism for the RPN83P app.
588+ The tolerance limit (` tol=1e-10 ` ) is hard coded and not configurable by the
589+ user. (I'm not sure if it's useful to make that configurable). I believe the HP
590+ calculators use the number of significant digits in its ` FIX ` mode to determine
591+ the tolerance limit. But TI calculator users tend not to use ` FIX ` modes,
592+ leaving the calculator with the maximum number of significant digits (i.e. the
593+ ` FIX(-) ` mode). I'm not sure that using the number of digits from the ` FIX ` mode
594+ is a reasonable mechanism for the RPN83P app.
530595
531596### Maximum Iterations
532597
533598To avoid an infinite loop, the maximum iterations used by the TVM Solver is
534599limited to ` TMAX ` . By default, ` TMAX ` is set to 15, but can be configured by the
535600user. Empirically, the TVM Solver (using the Secant method) seems to converge to
536- the tolerance of 1e-8 within about 7-8 iterations. So I set the default ` TMAX `
601+ the tolerance of 1e-10 within about 7-8 iterations. So I set the default ` TMAX `
537602to be about double that, which is where the 15 came from.
538603
539604### Convergence
0 commit comments