Die Rate Funktion von apache poi
besagt, dass es "// root von Newton Sekantenverfahren finden". Das ist Unsinn, da Secant method nur eine Quasi-Newton-Methode ist. Und "If the initial values are not close enough to the root, then there is no guarantee that the secant method converges.".
So ist den Standard guess
von 0.1
scheint nicht „nahe genug“ und so, wenn wir cell.setCellFormula("RATE(85.77534246575343, -1589.0, -18664.0, 5855586.0, 0, 0.06)");
verwenden - beachten Sie die explizite Einstellung von type
und guess
Eigenschaften und mit der guess
Eigenschaft „nahe genug“, um das Ergebnis der 0.05819488005
- dann dem Formel wird richtig ausgewertet.
Wenn apache poi
wirklich Newton's method verwenden würde, würde die Funktion auch mit dem Standard guess
von 0.1
richtig ausgewertet werden. Der Nachteil des Newton-Verfahrens besteht darin, dass es die Bewertung von f
und seiner Ableitung f′
bei jedem Schritt erfordert. Es kann also in manchen Fällen langsamer als die Secant-Methode sein.
Beispiel:
import java.io.FileOutputStream;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
public class ExcelRATEFunction {
private static double calculateRateNewton(double nper, double pmt, double pv, double fv, double type, double guess) {
int FINANCIAL_MAX_ITERATIONS = 20;
double FINANCIAL_PRECISION = 0.0000001;
double y, y1, xN = 0, f = 0, i = 0;
double rate = guess;
//find root by Newtons method (https://en.wikipedia.org/wiki/Newton%27s_method), not secant method!
//Formula see: https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_Derivation_of_Financial_Formulas#PV.2C_FV.2C_PMT.2C_NPER.2C_RATE
f = Math.pow(1 + rate, nper);
y = pv * f + pmt * ((f - 1)/rate) * (1 + rate * type) + fv;
//first derivative:
//y1 = (pmt * nper * type * Math.pow(rate,2) * f - pmt * f - pmt * rate * f + pmt * nper * rate * f + pmt * rate + pmt + nper * pv * Math.pow(rate,2) * f)/(Math.pow(rate,2) * (rate+1));
y1 = (f * ((pmt * nper * type + nper * pv) * Math.pow(rate,2) + (pmt * nper - pmt) * rate - pmt) + pmt * rate + pmt)/(Math.pow(rate,3) + Math.pow(rate,2));
xN = rate - y/y1;
while ((Math.abs(rate - xN) > FINANCIAL_PRECISION) && (i < FINANCIAL_MAX_ITERATIONS)) {
rate = xN;
f = Math.pow(1 + rate, nper);
y = pv * f + pmt * ((f - 1)/rate) * (1 + rate * type) + fv;
//first derivative:
//y1 = (pmt * nper * type * Math.pow(rate,2) * f - pmt * f - pmt * rate * f + pmt * nper * rate * f + pmt * rate + pmt + nper * pv * Math.pow(rate,2) * f)/(Math.pow(rate,2) * (rate+1));
y1 = (f * ((pmt * nper * type + nper * pv) * Math.pow(rate,2) + (pmt * nper - pmt) * rate - pmt) + pmt * rate + pmt)/(Math.pow(rate,3) + Math.pow(rate,2));
xN = rate - y/y1;
++i;
System.out.println(rate+", "+xN+", "+y+", "+y1);
}
rate = xN;
return rate;
}
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(1);
Cell cell = row.createCell(1);
cell.setCellFormula("RATE(85.77534246575343, -1589.0, -18664.0, 5855586.0, 0, 0.06)");
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
CellType celltype = evaluator.evaluateFormulaCellEnum(cell);
double value = 0.0;
if (celltype == CellType.NUMERIC) {
value = cell.getNumericCellValue();
System.out.println(value);
}
workbook.setForceFormulaRecalculation(true);
value = calculateRateNewton(85.77534246575343, -1589.0, -18664.0, 5855586.0, 0, 0.1);
System.out.println(value);
workbook.write(new FileOutputStream("ExcelRATEFunction.xlsx"));
workbook.close();
}
}