JavaScript中的金融利率函数无法正常工作

3
给出的函数simple financial rate function in javascript在某些情况下与Excel中的利率函数计算结果不同。它对于http://allfinancialmatters.com/2009/11/03/how-to-use-the-rate-function-in-excel/中给出的问题完美地工作,但是对于我的测试用例,它的结果与Excel中的利率计算结果不同。这种行为很奇怪,我无法解决这个问题。我的测试用例(包括Excel的输出)如下:
RATE(360,-665.3, 99000)    = 0.0059
RATE(360,-958.63, 192000)    =0.0036
RATE(180,-1302.96,192000)    = 0.0023
RATE(360, -889.19, 192000) =0.00312
RATE(360, -1145.8, 240000) = 0.0033

我的code.js文件是:

function rate(paymentsPerYear, paymentAmount, presentValue, futureValue, dueEndOrBeginning, interest)
{
    //If interest, futureValue, dueEndorBeginning was not set, set now
    //if (interest == null) // not working here :D 
    if (isNaN(interest))
        interest = 0.1;
        //interest = 0.1;

    if (isNaN(futureValue))
        futureValue = 0;

    if (isNaN(dueEndOrBeginning))
        dueEndOrBeginning = 0;

    var FINANCIAL_MAX_ITERATIONS = 128;//Bet accuracy with 128
    var FINANCIAL_PRECISION = 0.0000001;//1.0e-8

    var y, y0, y1, x0, x1 = 0, f = 0, i = 0;
    var rate = interest; // initiallizing rate to our guess interest 
    if (Math.abs(rate) < FINANCIAL_PRECISION)
    {
        y = presentValue * (1 + paymentsPerYear * rate) + paymentAmount * (1 + rate * dueEndOrBeginning) * paymentsPerYear + futureValue;
    }
    else
    {
        f = Math.exp(paymentsPerYear * Math.log(1 + rate));
        y = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
    }
    y0 = presentValue + paymentAmount * paymentsPerYear + futureValue;
    y1 = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;

    // find root by Newton secant method
    i = x0 = 0.0;
    x1 = rate;
    while ((Math.abs(y0 - y1) > FINANCIAL_PRECISION)
        && (i < FINANCIAL_MAX_ITERATIONS))
    {
        rate = (y1 * x0 - y0 * x1) / (y1 - y0);
        x0 = x1;
        x1 = rate;

        if (Math.abs(rate) < FINANCIAL_PRECISION)
        {
            y = presentValue * (1 + paymentsPerYear * rate) + paymentAmount * (1 + rate * dueEndOrBeginning) * paymentsPerYear + futureValue;
        }
        else
        {
            f = Math.exp(paymentsPerYear * Math.log(1 + rate));
            y = presentValue * f + paymentAmount * (1 / rate + dueEndOrBeginning) * (f - 1) + futureValue;
        }

        y0 = y1;
        y1 = y;
        ++i;
    }
    return rate;
    //return String(parseFloat(rate).toFixed(3)); // rounding it to 3 decimal places
    //return parseFloat(rate).toFixed(3);
}

我的 HTML 文件是

<head><title>JavaScript Loan Calculator</title>
<script src="code.js"></script>
</head>
<body bgcolor="white">

<form name="loandata">
  <table>

    <tr>
      <td>1)</td>
      <td>paymentsPerYear:</td>
      <td><input type="text" name="paymentsPerYear" size="12" 
                 onchange="calculate();"></td>
    </tr>
    <tr>
      <td>2)</td>
      <td>paymentAmount:</td>
      <td><input type="text" name="paymentAmount" size="12" 
                 onchange="calculate();"></td>
    </tr>
    <tr>
      <td>3)</td>
      <td>presentValue:</td>
      <td><input type="text" name="presentValue" size="12" 
                 onchange="calculate();"></td>
    </tr>


    <tr>
      <td>4)</td>
      <td>futureValue:</td>
      <td><input type="text" name="futureValue" size="12"></td>
    </tr>
    <tr>
      <td>5)</td>
      <td>dueEndOrBeginning:</td>
      <td><input type="text" name="dueEndOrBeginning" size="12"></td>
    </tr>
    <tr>
      <td>6)</td>
      <td>interest:</td>
      <td><input type="text" name="interest" size="12"></td>
    </tr>
    <tr><td colspan="3">
      <input type="button" value="Compute" onClick="calculate();">
    </td></tr>
        <tr>

      <td>APR:</td>
      <td><input type="text" name="APR" id="APR" size="12"></td>
    </tr>
  </table>
</form>


<script language="JavaScript">
function calculate() {
    var paymentsPerYear = document.loandata.paymentsPerYear.value;
    var paymentAmount = document.loandata.paymentAmount.value;
    var presentValue = document.loandata.presentValue.value;
    var futureValue = document.loandata.futureValue.value;
    var dueEndOrBeginning = document.loandata.dueEndOrBeginning.value ;
    var interest = document.loandata.interest.value ;
    var ans = rate(parseFloat(paymentsPerYear), parseFloat(paymentAmount), parseFloat(presentValue), parseFloat(futureValue), parseFloat(dueEndOrBeginning), parseFloat(interest));
    document.loandata.APR.value=ans;
    //alert(futureValue);


}

</script>
</body>
</html>

1
“不时地给出与Excel利率函数不同的答案” - 所以是指有时候只是相差几个小数点,还是完全不同? - nnnnnn
在我的测试用例中,有时候会出现根本不同的答案...而且使用js文件得到的答案是错误的。但是,如果使用http://allfinancialmatters.com/2009/11/03/how-to-use-the-rate-function-in-excel/上提供的数据,我的答案就是一致的。 - Abdul Rauf
需要隔离产生不同结果的值...然后创建一些简单的测试数组,使用代码中的断点逐步执行并查看可能导致问题的原因。 - charlietfl
1
乍一看,代码行var FINANCIAL_MAX_ITERATIONS = 128;//Bet accuracy with 128是错误的:“如果RATE函数的连续结果在20次迭代后不能收敛到0.0000001以内”http://office.microsoft.com/en-gb/excel-help/rate-function-HA102752889.aspx - SheetJS
1
@Hugo 不好意思,我已经停止在这个项目上的工作了。 - Abdul Rauf
显示剩余2条评论
1个回答

5

如果有人仍在寻找Excel的Rate函数的javascript实现,这是我想出来的:

var rate = function(nper, pmt, pv, fv, type, guess) {
    // Sets default values for missing parameters
    fv = typeof fv !== 'undefined' ? fv : 0;
    type = typeof type !== 'undefined' ? type : 0;
    guess = typeof guess !== 'undefined' ? guess : 0.1;

    // Sets the limits for possible guesses to any
    // number between 0% and 100%
    var lowLimit = 0;
    var highLimit = 1;

   // Defines a tolerance of up to +/- 0.00005% of pmt, to accept
   // the solution as valid.
   var tolerance = Math.abs(0.00000005 * pmt);

   // Tries at most 40 times to find a solution within the tolerance.
   for (var i = 0; i < 40; i++) {
       // Resets the balance to the original pv.
       var balance = pv;

       // Calculates the balance at the end of the loan, based
       // on loan conditions.
       for (var j = 0; j < nper; j++ ) {
           if (type == 0) {
               // Interests applied before payment
               balance = balance * (1 + guess) + pmt;
           } else {
               // Payments applied before insterests
               balance = (balance + pmt) * (1 + guess);
           }
       }

       // Returns the guess if balance is within tolerance.  If not, adjusts
       // the limits and starts with a new guess.
       if (Math.abs(balance + fv) < tolerance) {
           return guess;
       } else if (balance + fv > 0) {
           // Sets a new highLimit knowing that
           // the current guess was too big.
           highLimit = guess;
       } else  {
           // Sets a new lowLimit knowing that
           // the current guess was too small.
           lowLimit = guess;
       }

       // Calculates the new guess.
       guess = (highLimit + lowLimit) / 2;
   }

   // Returns null if no acceptable result was found after 40 tries.
   return null;
};

使用Abdul的测试用例测试该功能,得到以下结果:

rate(360,-665.3, 99000);
0.005916521358085446

rate(360,-958.63, 192000);
0.0036458502960158515

rate(180,-1302.96,192000);
0.0022917255526408564

rate(360, -889.19, 192000);
0.0031250616819306744

rate(360, -1145.8, 240000);
0.003333353153720964

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接