上一版实现:https://www.cnblogs.com/Alex-Zeng/p/9334582.html

上一版实现在某些情况下会计算出负值,与Excel的计算有差异。所以修改了实现方法。

实现的具体代码如下:

function IRR_ZEN(p_amount_array in typ_cashflow_array) RETURN NUMBER is
    rtn_err      number := 0;
    irrGuess     number := 0.01; -- default: 10%
    irr          number := 0.0;
    minDistance  number := 1E-15; --iteration: the smaller the distance, the smaller the interpolation
    maxIteration integer := 1000;

    wasHi         boolean := false;
    noOfCashFlows integer := 0;
    cashValue     number := 0.0;
    cashFlowStart number := 0.0;

    result_value number := 0.0;

  BEGIN
    irr           := irrGuess;
    result_value  := -999999;
    noOfCashFlows := p_amount_array.count;

    -- business startup costs
    cashFlowStart := p_amount_array(1);

    for i in 1 .. maxIteration loop
      -- calculate cash value with current irr:
      cashValue := cashFlowStart; -- init with startup costs

      -- for each cash flow
      for j in 2 .. noOfCashFlows loop
        cashValue := cashValue + p_amount_array(j) / power(1.0 + irr, j-1);
      end loop;

      -- cash value is nearly zero
      if (abs(cashValue) < 0.0000000001) then
        -- dbms_output.put_line('abs(cashValue) < 0.0000000001 : '||to_char(cashValue));
        result_value := irr;
        exit;
      end if;

      -- adjust irr for next iteration:
      -- cash value > 0 => next irr > current irr
      if (cashValue > 0) then
        if wasHi then
          irrGuess := irrGuess / 2;
        end if;

        irr := irr + irrGuess;

        if wasHi then
          irrGuess := irrGuess - minDistance;
          wasHi    := false;
        end if;

      else
        -- cash value < 0 => next irr < current irr
        irrGuess := irrGuess / 2;
        irr      := irr - irrGuess;
        wasHi    := true;
      end if;

      -- estimated result too small to continue => end
      -- calculation
      if (irrGuess <= minDistance) then
        -- dbms_output.put_line('irrGuess <= minDistance : '||to_char(irrGuess));
        result_value := irr;
        exit;
      end if;
    end loop;

    return result_value;
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line(substr(sqlerrm, 1, 640));
      return rtn_err;
  END IRR_ZEN;
12-18 22:45