2010-07-07 188 views
6

我正在使用PHP轉換抵押計算器,但我不一定需要PHP解決方案。我正在尋找複製Excel RATE函數所需的邏輯。我找到了一種使用二分法的解決方案,如果情況變得更糟,我會使用它。使用牛頓法重新創建Excel RATE函數

我知道interwebs世界中有人有這樣的功能的知識,所以我很樂意有一個簡單的答案,而不是從頭開始創建一個解決方案。

參考文獻:

由於

回答

8

使用正割法(牛頓方法的有限差分近似)的MS Excel的RATE()函數的執行情況取自PHPExcel:

define('FINANCIAL_MAX_ITERATIONS', 128); 
define('FINANCIAL_PRECISION', 1.0e-08); 


function RATE($nper, $pmt, $pv, $fv = 0.0, $type = 0, $guess = 0.1) { 

    $rate = $guess; 
    if (abs($rate) < FINANCIAL_PRECISION) { 
     $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv; 
    } else { 
     $f = exp($nper * log(1 + $rate)); 
     $y = $pv * $f + $pmt * (1/$rate + $type) * ($f - 1) + $fv; 
    } 
    $y0 = $pv + $pmt * $nper + $fv; 
    $y1 = $pv * $f + $pmt * (1/$rate + $type) * ($f - 1) + $fv; 

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

     if (abs($rate) < FINANCIAL_PRECISION) { 
      $y = $pv * (1 + $nper * $rate) + $pmt * (1 + $rate * $type) * $nper + $fv; 
     } else { 
      $f = exp($nper * log(1 + $rate)); 
      $y = $pv * $f + $pmt * (1/$rate + $type) * ($f - 1) + $fv; 
     } 

     $y0 = $y1; 
     $y1 = $y; 
     ++$i; 
    } 
    return $rate; 
} // function RATE() 
+2

完美完美!只要記住人們爲FINANCIAL_PRECISION和FINANCIAL_MAX_ITERATIONS插入一些DEFINE語句 - 或者 - 分別用這些靜態值0.0000001和20替換它們。我在上面引用的MS網站上找到了這些值。 – Exit 2010-07-07 21:59:36

+0

此方法有收斂問題,請參閱https://stackoverflow.com/questions/14031208/apache-po-rate-formula-inconsistency-with-long-periods – sled 2017-09-25 11:38:18

+0

@Exit - 那麼也許您會考慮提供更好的解決方案,分享你的知識將有利於我們所有人 – 2017-09-25 11:39:59

0

我試過使用上面的代碼,但結果與Excel(或Google Spreadsheet)不盡相同。

我不知道你是否需要實現這個功能,但無論如何,我看着這個算法是如何構建的,即使我無法訪問Excel的源代碼(或谷歌工作表),我發現這不是一個簡單的計算。關於這道數學,更可以在這裏閱讀:

https://brownmath.com/bsci/loan.htm#Eq8

功能,在PHP中,可能是這樣的:

function rate($nprest, $vlrparc, $vp, $guess = 0.25) { 
    $maxit = 100; 
    $precision = 14; 
    $guess = round($guess,$precision); 
    for ($i=0 ; $i<$maxit ; $i++) { 
     $divdnd = $vlrparc - ($vlrparc * (pow(1 + $guess , -$nprest))) - ($vp * $guess); 
     $divisor = $nprest * $vlrparc * pow(1 + $guess , (-$nprest - 1)) - $vp; 
     $newguess = $guess - ($divdnd/$divisor); 
     $newguess = round($newguess, $precision); 
     if ($newguess == $guess) { 
      return $newguess; 
     } else { 
      $guess = $newguess; 
     } 
    } 
    return null; 
}