PHPExcel based function RATE() returning NAN()

匿名 (未验证) 提交于 2019-12-03 09:17:17

问题:

I have this code: http://pastebin.com/Sd9WKZFr

When i call something like rate(60, -6000, 120000) it returns me a NAN result, but the same function on MS Excel returns me 0,04678.... I have the same problem trying -5000, -4000, -3000 and -2000.

When i debug the code, i see that about the 8/9 iteration, the line number 29 begins to return a NAN result, making all of other results to turn NAN too.

BUT, when i call something like rate(60, -1000, 120000) it returns me a float -0.02044..., exactly the same result of MS Excel.

I have already tryed to convert all of math calculations into BCMath functions, but this way the results of -6000 is wrong (-1.0427... instead of 0,04678...) but using -1000 the result is correct, matching excel's result.

Is there a way to make it work correctly?

Thanks in advance for any useful sight about that.

回答1:

I'll need to do some tests to ensure that there's no adverse effects in other situations; but the following looks as though it might fix this problem, and certainly calculates the correct rate value for your arguments RATE(60, -6000, 120000) stabilises at 0.046781916422493 in iteration 15.

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 (($nper * abs($pmt)) > ($pv - $fv))             $x1 = abs($x1);          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:

I would not suggest using Secant method to find internal rate of return as it consumes more time than other preferred iterative methods such as Newton Raphson method. From the code it seems setting a maximum of 128 iterations is a waste of time

Using Newton Raphson method to find RATE with either of the two TVM equations takes only 3 iterations

TVM Eq. 1: PV(1+i)^N + PMT(1+i*type)[(1+i)^N -1]/i + FV = 0  f(i) = 0 + -6000 * (1 + i * 0) [(1+i)^60 - 1)]/i + 120000 * (1+i)^60  f'(i) = (-6000 * ( 60 * i * (1 + i)^(59+0) - (1 + i)^60) + 1) / (i * i)) + 60 * 120000 * (1+0.05)^59  i0 = 0.05 f(i1) = 120000 f'(i1) = 42430046.1459 i1 = 0.05 - 120000/42430046.1459 = 0.0471718154728 Error Bound = 0.0471718154728 - 0.05 = 0.002828 > 0.000001  i1 = 0.0471718154728 f(i2) = 12884.8972 f'(i2) = 33595275.7358 i2 = 0.0471718154728 - 12884.8972/33595275.7358 = 0.0467882824629 Error Bound = 0.0467882824629 - 0.0471718154728 = 0.000384 > 0.000001  i2 = 0.0467882824629 f(i3) = 206.9714 f'(i3) = 32520602.801 i3 = 0.0467882824629 - 206.9714/32520602.801 = 0.0467819181458 Error Bound = 0.0467819181458 - 0.0467882824629 = 6.0E-6 > 0.000001  i3 = 0.0467819181458 f(i4) = 0.056 f'(i4) = 32503002.4159 i4 = 0.0467819181458 - 0.056/32503002.4159 = 0.0467819164225 Error Bound = 0.0467819164225 - 0.0467819181458 = 0 < 0.000001 IRR = 4.68%   TVM Eq. 2: PV + PMT(1+i*type)[1-{(1+i)^-N}]/i + FV(1+i)^-N = 0  f(i) = 120000 + -6000 * (1 + i * 0) [1 - (1+i)^-60)]/i + 0 * (1+i)^-60  f'(i) = (--6000 * (1+i)^-60 * ((1+i)^60 - 60 * i - 1) /(i*i)) + (0 * -60 * (1+i)^(-60-1))  i0 = 0.05 f(i1) = 6424.2628 f'(i1) = 1886058.972 i1 = 0.05 - 6424.2628/1886058.972 = 0.0465938165535 Error Bound = 0.0465938165535 - 0.05 = 0.003406 > 0.000001  i1 = 0.0465938165535 f(i2) = -394.592 f'(i2) = 2081246.2069 i2 = 0.0465938165535 - -394.592/2081246.2069 = 0.046783410646 Error Bound = 0.046783410646 - 0.0465938165535 = 0.00019 > 0.000001  i2 = 0.046783410646 f(i3) = 3.1258 f'(i3) = 2069722.0554 i3 = 0.046783410646 - 3.1258/2069722.0554 = 0.0467819004105 Error Bound = 0.0467819004105 - 0.046783410646 = 2.0E-6 > 0.000001  i3 = 0.0467819004105 f(i4) = -0.0335 f'(i4) = 2069813.5309 i4 = 0.0467819004105 - -0.0335/2069813.5309 = 0.0467819165937 Error Bound = 0.0467819165937 - 0.0467819004105 = 0 < 0.000001 IRR = 4.68% 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!