历史版本6 :内部收益率公式IRR 返回文档
编辑时间: 内容长度:图片数:目录数: 修改原因:

目录:

1. 描述编辑

场景描述:内部收益率IRR这个公式在可行性评估分析类的财务报表中频繁的被使用,FR中默认没有这个公式,而这个值的计算又不是纯数学计算,需要靠猜测和差值最终得到一个理想的结果。

下面就介绍如何实现。

2.思路编辑

我们可以根据IRR逻辑创建一个自定义函数,然后根据一个数据列的集合来计算IRR内部收益率。

3. 操作步骤编辑

在一般情况下,若您希望在FineReport设计器基础上进行二次开发,我们可以在Eclipse或idea中通过调用FineReport启动类来启动设计器,以便于开发过程中的调试和二次开发的准备。

注意需要引入FineReport设计器下的jar包,详细点击Eclipse中启动设计器

3.1 完整代码如下

package com.midas.function.excel; import java.math.BigDecimal; import com.fr.general.FArray; import com.fr.general.GeneralUtils; import com.fr.script.AbstractFunction; public class IRR extends AbstractFunction { private static final long serialVersionUID = 7634415917398642321L; private static final String ERROR_VALUE = "#NUM!"; @Override public Object run(Object[] args) { try{ if(1 == args.length){ return run( transArr( (FArray) args[0] ) ); }else if(2 == args.length){ return run( transArr( (FArray) args[0] ), trans( args[1] ) ); } }catch(Exception e){ System.out.println(e); } return ERROR_VALUE; } /** * 将其他类型的数字转换为大数(保证精度) * @param ele * @return */ private static BigDecimal trans(Object ele){ try{ String val = GeneralUtils.objectToString(ele); return new BigDecimal(val); }catch(Exception e){ } return (BigDecimal) ele; } /** * 将数组转换为大数数组 * @param in * @return */ private static FArray<BigDecimal> transArr(FArray in){ FArray<BigDecimal> rt = new FArray<BigDecimal>(); for(int i=0;i<in.length();i++){ Object ele = in.elementAt(i); rt.add(trans(ele)); } return rt; } private static BigDecimal run(FArray<BigDecimal> cashflow){ return run( cashflow, new BigDecimal(0.1d) ); } private static BigDecimal run(FArray<BigDecimal> cashflow,BigDecimal guess){ BigDecimal maxrate = initRateMax(cashflow,guess); BigDecimal minrate = initRateMin(cashflow,guess); for( int i=0; i<Init_Max_Loop; i++ ){ BigDecimal testrate = minrate.add(maxrate).divide( new BigDecimal(2d) ); BigDecimal npv = NPV( cashflow, testrate ); if( npv.abs().compareTo(Accuracy) == LESS ){ guess = testrate; break; }else if( npv.compareTo(ZERO) == LESS ){ minrate = testrate; }else{ maxrate = testrate; } } //保留16位小数(足够精度) return guess.setScale(16,BigDecimal.ROUND_HALF_UP); } //最小精度 private static final BigDecimal Accuracy = new BigDecimal(0.00000001d); //最大循环次数,excel用的是20次,不过精度只到小数点后两位,而且不一定一定能算出值,为了尽可能保证算出结果,我增加到100次, private static final int Init_Max_Loop = 100; private static final BigDecimal ZERO = new BigDecimal(0); private static final BigDecimal ONE = new BigDecimal(1); private static final BigDecimal Z005 = new BigDecimal(0.005d); private static final BigDecimal Z2 = new BigDecimal(0.2d); private static final int GREATER = 1; private static final int LESS = -1; /** * 生成一个使NPV为负数的R作为内部收益率下限值 * @param cashflow * @param guess * @return */ private static BigDecimal initRateMin(FArray<BigDecimal> cashflow,BigDecimal guess){ for( int i=0; i<Init_Max_Loop; i++ ){ BigDecimal npv = NPV( cashflow, guess ); if( npv.compareTo(ZERO) == LESS ){ return guess; } BigDecimal step = guess.abs().multiply( Z2 ); guess = guess.add( step.compareTo( Z005 ) == LESS ? Z005 : step ); } return guess; } /** * 生成一个使NPV为正数的R作为内部收益率的上限制 * @param cashflow * @param guess * @return */ private static BigDecimal initRateMax(FArray<BigDecimal> cashflow,BigDecimal guess){ for( int i=0; i<Init_Max_Loop; i++ ){ BigDecimal npv = NPV( cashflow, guess ); if( npv.compareTo(ZERO) == GREATER ){ return guess; } BigDecimal step = guess.abs().multiply( Z2 ); guess = guess.subtract( step.compareTo( Z005 ) == LESS ? Z005 : step ); } return guess; } /** * 算NPV * @param cashflow * @param rate * @return */ private static BigDecimal NPV(FArray<BigDecimal> cashflow,BigDecimal rate){ BigDecimal npv = ZERO; BigDecimal rpowj = ONE;//(1+r)^0 BigDecimal radd1 = rate.add(ONE);//1+r for( int j=0; j<cashflow.length(); j++ ){ BigDecimal valuej = cashflow.elementAt(j); npv = npv.add( valuej.divide( rpowj, 10, BigDecimal.ROUND_HALF_DOWN ) );// vj / (1+r)^j rpowj = rpowj.multiply(radd1); // (1+r)^j //npv += cashflow.elementAt(j) / Math.pow( 1+rate, j ); } return npv; } }
3.2 编译自定义函数
将编译后的IRR.class放到FineReport的安装目录WEB-INF下面的classes目录下,因为IRR.java属于包com.fr.function,所以IRR.class需要放到%FR_HOME%\webapps\webroot\WEB-INF\classes\com\fr\function目录下。
3.3 注册自定义函数
生成该函数的类后需要在设计器中进行注册,才可以使用该函数。打开服务器>函数管理器,选择刚刚定义好了IRR类,如下图:
222

3.4 使用自定义函数
注册好自定义函数后,制作报表时便可直接使用了,使用方法与内置的函数是相同的。

(1)excel中的IRR公式效果

222
(2)FR中的IRR公式效果
模板样式设计:

222
预览结果:
222
通过比较我们可以知道,在FR中使用该公式,比excel中更精确,也更稳定。

注:不容易出现算不出结果的情况。

4.移动端效果预览编辑


222