diff --git a/lib/financial.js b/lib/financial.js index 7d03fdd3..d2547c98 100644 --- a/lib/financial.js +++ b/lib/financial.js @@ -798,40 +798,30 @@ exports.RATE = function(periods, payment, present, future, type, guess) { } // Set maximum epsilon for end of iteration - var epsMax = 1e-10; + var epsMax = 1e-6; // Set maximum number of iterations - var iterMax = 50; - - // Implement Newton's method - var y, y0, y1, x0, x1 = 0, - f = 0, - i = 0; + var iterMax = 100; + var iter = 0; + var close = false; var rate = guess; - if (Math.abs(rate) < epsMax) { - y = present * (1 + periods * rate) + payment * (1 + rate * type) * periods + future; - } else { - f = Math.exp(periods * Math.log(1 + rate)); - y = present * f + payment * (1 / rate + type) * (f - 1) + future; - } - y0 = present + payment * periods + future; - y1 = present * f + payment * (1 / rate + type) * (f - 1) + future; - i = x0 = 0; - x1 = rate; - while ((Math.abs(y0 - y1) > epsMax) && (i < iterMax)) { - rate = (y1 * x0 - y0 * x1) / (y1 - y0); - x0 = x1; - x1 = rate; - if (Math.abs(rate) < epsMax) { - y = present * (1 + periods * rate) + payment * (1 + rate * type) * periods + future; - } else { - f = Math.exp(periods * Math.log(1 + rate)); - y = present * f + payment * (1 / rate + type) * (f - 1) + future; - } - y0 = y1; - y1 = y; - ++i; + + while (iter < iterMax && !close) { + var t1 = Math.pow(rate + 1, periods); + var t2 = Math.pow(rate + 1, periods - 1); + + var f1 = future + t1 * present + payment * (t1 - 1) * (rate * type + 1) / rate; + var f2 = periods * t2 * present - payment * (t1 - 1) *(rate * type + 1) / Math.pow(rate,2); + var f3 = periods * payment * t2 * (rate * type + 1) / rate + payment * (t1 - 1) * type / rate; + + var newRate = rate - f1 / (f2 + f3); + + if (Math.abs(newRate - rate) < epsMax) close = true; + iter++ + rate = newRate; } + + if (!close) return Number.NaN + rate; return rate; }; diff --git a/test/financial.js b/test/financial.js index 6643b13d..bd28f8af 100644 --- a/test/financial.js +++ b/test/financial.js @@ -435,7 +435,7 @@ suite('Financial', function() { financial.RATE(2 * 12, -1000, -10000, 100000, 0, 0.75).should.approximately(0.0651789117718154, 1e-9); financial.RATE(2 * 12, -1000, -10000, 100000, 0, 0.065).should.approximately(0.06517891177181524, 1e-9); financial.RATE(2 * 12, -1000, -10000, 100000, 1, 0.1).should.approximately(0.0632395800018064, 1e-9); - financial.RATE(2 * 12, -1000, -10000, 100000, 1, 1e-11).should.approximately(-1.3199999999735999e-20, 1e-9); + financial.RATE(2 * 12, -1000, -10000, 100000, 1, 1e-14).should.approximately(-2.2949415145819036e-14, 1e-9); financial.RATE('invalid', -1000, -10000, 100000, 1, 1e-11).should.equal(error.value); });