为了模拟excel的速率函数,我正在使用apache poi速率函数,我从SVN中获取:
private double calculateRate(double nper, double pmt, double pv, double fv, double type, double guess) {
//FROM MS http://office.microsoft.com/en-us/excel-help/rate-HP005209232.aspx
int FINANCIAL_MAX_ITERATIONS = 20; //Bet accuracy with 128
double FINANCIAL_PRECISION = 0.0000001; //1.0e-8
double y, y0, y1, x0, x1 = 0, f = 0, i = 0;
double rate = guess;
if (Math.abs(rate) < FINANCIAL_PRECISION) {
y = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
}
else {
f = Math.exp(nper * Math.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 the 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 = pv * (1 + nper * rate) + pmt * (1 + rate * type) * nper + fv;
}
else {
f = Math.exp(nper * Math.log(1 + rate));
y = pv * f + pmt * (1 / rate + type) * (f - 1) + fv;
}
y0 = y1;
y1 = y;
++i;
}
return rate;
}
对于计算(120,28.1,-2400,0,0,0.1)),输出与Excel相同: 0.599
但是,如果我尝试相同的计算,这次是带有值:
的calculateRate(360, 15.9, -2400, 0, 0, 0.1))
在Excel中我获得 0.580 ,并且程序返回 -1.1500428517726355 。有提示吗?
您在问题中粘贴的代码有很多错误。
它假定始终找到率(不是真的),并且在找不到费率的情况下没有任何规定。
某些语句将通过使用更合适的编程语句来避免的错误。例如,从您的代码中获取以下语句:
f = Math.exp(nper * Math.log(1 + rate));
试图找到负值或零值的日志时,这将丢失错误。它本可以被重写为
f = Math.pow(1 + rate, nper);
迭代计算中的评论指出它正在编程割线方法,但是检查了迭代计算是否有错误的变量收敛。当应该测试利率收敛时,它正在测试未来值的收敛。
我在记事本中复制了您的代码,并删除了Java的变量声明,并用JavaScript变量声明代替了这些声明,以使用您的示例数据测试代码。正如我所说,该代码在第二次迭代中停止,因为未来值的差异不存在错误,并且由于没有测试可以查看是否找到利率,因此代码返回了利率和一个。这是错误的。
我不确定为什么此代码在报告正确的速率的情况下可以像第一个数据集一样。我建议以正确的方式重新编码该功能。
public double rate(double nper, double pmt, double pv)
{
//System.out.println("function rate : " + nper + " " + pmt + " pv " + pv);
double error = 0.0000001;
double high = 1.00;
double low = 0.00;
double rate = (2.0 * (nper * pmt - pv)) / (pv * nper);
while(true) {
// Check for error margin
double calc = Math.pow(1 + rate, nper);
calc = (rate * calc) / (calc - 1.0);
calc -= pmt / pv;
if (calc > error) {
// Guess is too high, lower the guess
high = rate;
rate = (high + low) / 2;
}
else if (calc < -error) {
// Guess is too low, higher the guess.
low = rate;
rate = (high + low) / 2;
}
else {
// Acceptable guess
break;
}
}
//System.out.println("Rate : " + rate);
return rate;
}
示例:=RATE(60, 2112500, 65000000)
返回0.025198;与Excel(正确)相同。