Support for IRR() function, see Bugzilla 50409. Includes fix for Bugzilla 50437
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1044370 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b2ee6fb309
commit
119bc57e05
@ -35,7 +35,7 @@
|
|||||||
<section><title>29 October 2010 - POI 3.7 available</title>
|
<section><title>29 October 2010 - POI 3.7 available</title>
|
||||||
<p>The Apache POI team is pleased to announce the release of 3.7. This includes a large number of bug fixes, and some
|
<p>The Apache POI team is pleased to announce the release of 3.7. This includes a large number of bug fixes, and some
|
||||||
enhancements (especially text extraction). See the
|
enhancements (especially text extraction). See the
|
||||||
<link href="http://www.apache.org/dist/poi/release/bin/RELEASE-NOTES.txt">full release notes</link> for more details.
|
<link href="http://www.apache.org/dist/poi/release/bin/RELEASE_NOTES.txt">full release notes</link> for more details.
|
||||||
</p>
|
</p>
|
||||||
<p>A full list of changes is available in the <link href="changes.html">change log</link>.
|
<p>A full list of changes is available in the <link href="changes.html">change log</link>.
|
||||||
People interested should also follow the <link href="mailinglists.html">dev mailing list</link> to track further progress.</p>
|
People interested should also follow the <link href="mailinglists.html">dev mailing list</link> to track further progress.</p>
|
||||||
|
@ -34,8 +34,10 @@
|
|||||||
|
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.8-beta1" date="2010-??-??">
|
<release version="3.8-beta1" date="2010-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">50437 - Support passing ranges to NPV()</action>
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">50409 - Added implementation for IRR()</action>
|
||||||
<action dev="poi-developers" type="add">47405 - Improved performance of RowRecordsAggregate.getStartRowNumberForBlock / getEndRowNumberForBlock</action>
|
<action dev="poi-developers" type="add">47405 - Improved performance of RowRecordsAggregate.getStartRowNumberForBlock / getEndRowNumberForBlock</action>
|
||||||
<action dev="poi-developers" type="add">50315 - Avoid crashing Excel when sorting XSSFSheet autofilter</action>
|
<action dev="poi-developers" type="fix">50315 - Avoid crashing Excel when sorting XSSFSheet autofilter</action>
|
||||||
<action dev="poi-developers" type="add">50076 - Allow access from XSSFReader to sheet comments and headers/footers</action>
|
<action dev="poi-developers" type="add">50076 - Allow access from XSSFReader to sheet comments and headers/footers</action>
|
||||||
<action dev="poi-developers" type="add">50076 - Refactor XSSFEventBasedExcelExtractor to make it easier for you to have control over outputting the cell contents</action>
|
<action dev="poi-developers" type="add">50076 - Refactor XSSFEventBasedExcelExtractor to make it easier for you to have control over outputting the cell contents</action>
|
||||||
<action dev="poi-developers" type="fix">50258 - avoid corruption of XSSFWorkbook after applying XSSFRichTextRun#applyFont</action>
|
<action dev="poi-developers" type="fix">50258 - avoid corruption of XSSFWorkbook after applying XSSFRichTextRun#applyFont</action>
|
||||||
|
@ -100,6 +100,7 @@ public final class FunctionEval {
|
|||||||
retval[58] = FinanceFunction.NPER;
|
retval[58] = FinanceFunction.NPER;
|
||||||
retval[59] = FinanceFunction.PMT;
|
retval[59] = FinanceFunction.PMT;
|
||||||
|
|
||||||
|
retval[62] = new Irr();
|
||||||
retval[63] = NumericFunction.RAND;
|
retval[63] = NumericFunction.RAND;
|
||||||
retval[64] = new Match();
|
retval[64] = new Match();
|
||||||
retval[65] = DateFunc.instance;
|
retval[65] = DateFunc.instance;
|
||||||
|
@ -67,7 +67,7 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction {
|
|||||||
return new NumberEval(result);
|
return new NumberEval(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static final class ValueCollector extends MultiOperandNumericFunction {
|
static final class ValueCollector extends MultiOperandNumericFunction {
|
||||||
private static final ValueCollector instance = new ValueCollector();
|
private static final ValueCollector instance = new ValueCollector();
|
||||||
public ValueCollector() {
|
public ValueCollector() {
|
||||||
super(false, false);
|
super(false, false);
|
||||||
|
@ -56,7 +56,7 @@ package org.apache.poi.ss.formula.functions;
|
|||||||
* ny + p + f=0 ...{when r=0}
|
* ny + p + f=0 ...{when r=0}
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
final class FinanceLib {
|
public final class FinanceLib {
|
||||||
|
|
||||||
private FinanceLib() {
|
private FinanceLib() {
|
||||||
// no instances of this class
|
// no instances of this class
|
||||||
|
120
src/java/org/apache/poi/ss/formula/functions/Irr.java
Normal file
120
src/java/org/apache/poi/ss/formula/functions/Irr.java
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.ss.formula.functions;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.formula.eval.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the internal rate of return.
|
||||||
|
*
|
||||||
|
* Syntax is IRR(values) or IRR(values,guess)
|
||||||
|
*
|
||||||
|
* @author Marcel May
|
||||||
|
* @author Yegor Kozlov
|
||||||
|
*
|
||||||
|
* @see <a href="http://en.wikipedia.org/wiki/Internal_rate_of_return#Numerical_solution">Wikipedia on IRR</a>
|
||||||
|
* @see <a href="http://office.microsoft.com/en-us/excel-help/irr-HP005209146.aspx">Excel IRR</a>
|
||||||
|
*/
|
||||||
|
public final class Irr implements Function {
|
||||||
|
|
||||||
|
|
||||||
|
public ValueEval evaluate(final ValueEval[] args, final int srcRowIndex, final int srcColumnIndex) {
|
||||||
|
if(args.length == 0 || args.length > 2) {
|
||||||
|
// Wrong number of arguments
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double[] values = AggregateFunction.ValueCollector.collectValues(args[0]);
|
||||||
|
double guess;
|
||||||
|
if(args.length == 2) {
|
||||||
|
guess = NumericFunction.singleOperandEvaluate(args[1], srcRowIndex, srcColumnIndex);
|
||||||
|
} else {
|
||||||
|
guess = 0.1d;
|
||||||
|
}
|
||||||
|
double result = irr(values, guess);
|
||||||
|
NumericFunction.checkValue(result);
|
||||||
|
return new NumberEval(result);
|
||||||
|
} catch (EvaluationException e){
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes the internal rate of return using an estimated irr of 10 percent.
|
||||||
|
*
|
||||||
|
* @param income the income values.
|
||||||
|
* @return the irr.
|
||||||
|
*/
|
||||||
|
public static double irr(double[] income) {
|
||||||
|
return irr(income, 0.1d);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates IRR using the Newton-Raphson Method.
|
||||||
|
* <p>
|
||||||
|
* Starting with the guess, the method cycles through the calculation until the result
|
||||||
|
* is accurate within 0.00001 percent. If IRR can't find a result that works
|
||||||
|
* after 20 tries, the Double.NaN<> is returned.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* The implementation is inspired by the NewtonSolver from the Apache Commons-Math library,
|
||||||
|
* @see {http://commons.apache.org/}
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param values the income values.
|
||||||
|
* @param guess the initial guess of irr.
|
||||||
|
* @return the irr value. The method returns <code>Double.NaN</code>
|
||||||
|
* if the maximum iteration count is exceeded
|
||||||
|
*
|
||||||
|
* @see {http://en.wikipedia.org/wiki/Internal_rate_of_return#Numerical_solution}
|
||||||
|
* @see {http://en.wikipedia.org/wiki/Newton%27s_method}
|
||||||
|
*/
|
||||||
|
public static double irr(double[] values, double guess) {
|
||||||
|
int maxIterationCount = 20;
|
||||||
|
double absoluteAccuracy = 1E-7;
|
||||||
|
|
||||||
|
double x0 = guess;
|
||||||
|
double x1;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (i < maxIterationCount) {
|
||||||
|
|
||||||
|
// the value of the function (NPV) and its derivate can be calculated in the same loop
|
||||||
|
double fValue = 0;
|
||||||
|
double fDerivative = 0;
|
||||||
|
for (int k = 0; k < values.length; k++) {
|
||||||
|
fValue += values[k] / Math.pow(1.0 + x0, k);
|
||||||
|
fDerivative += -k * values[k] / Math.pow(1.0 + x0, k + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the essense of the Newton-Raphson Method
|
||||||
|
x1 = x0 - fValue/fDerivative;
|
||||||
|
|
||||||
|
if (Math.abs(x1 - x0) <= absoluteAccuracy) {
|
||||||
|
return x1;
|
||||||
|
}
|
||||||
|
|
||||||
|
x0 = x1;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
// maximum number of iterations is exceeded
|
||||||
|
return Double.NaN;
|
||||||
|
}
|
||||||
|
}
|
@ -17,11 +17,14 @@
|
|||||||
|
|
||||||
package org.apache.poi.ss.formula.functions;
|
package org.apache.poi.ss.formula.functions;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.formula.TwoDEval;
|
||||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||||
import org.apache.poi.ss.formula.eval.EvaluationException;
|
import org.apache.poi.ss.formula.eval.EvaluationException;
|
||||||
import org.apache.poi.ss.formula.eval.NumberEval;
|
import org.apache.poi.ss.formula.eval.NumberEval;
|
||||||
import org.apache.poi.ss.formula.eval.ValueEval;
|
import org.apache.poi.ss.formula.eval.ValueEval;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the net present value of an investment by using a discount rate
|
* Calculates the net present value of an investment by using a discount rate
|
||||||
* and a series of future payments (negative values) and income (positive
|
* and a series of future payments (negative values) and income (positive
|
||||||
@ -30,78 +33,27 @@ import org.apache.poi.ss.formula.eval.ValueEval;
|
|||||||
* income.
|
* income.
|
||||||
*
|
*
|
||||||
* @author SPetrakovsky
|
* @author SPetrakovsky
|
||||||
|
* @author Marcel May
|
||||||
*/
|
*/
|
||||||
public final class Npv implements Function2Arg, Function3Arg, Function4Arg {
|
public final class Npv implements Function {
|
||||||
|
|
||||||
|
|
||||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1) {
|
|
||||||
double result;
|
|
||||||
try {
|
|
||||||
double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
|
||||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
|
||||||
result = evaluate(rate, d1);
|
|
||||||
NumericFunction.checkValue(result);
|
|
||||||
} catch (EvaluationException e) {
|
|
||||||
return e.getErrorEval();
|
|
||||||
}
|
|
||||||
return new NumberEval(result);
|
|
||||||
}
|
|
||||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
|
||||||
ValueEval arg2) {
|
|
||||||
double result;
|
|
||||||
try {
|
|
||||||
double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
|
||||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
|
||||||
double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex);
|
|
||||||
result = evaluate(rate, d1, d2);
|
|
||||||
NumericFunction.checkValue(result);
|
|
||||||
} catch (EvaluationException e) {
|
|
||||||
return e.getErrorEval();
|
|
||||||
}
|
|
||||||
return new NumberEval(result);
|
|
||||||
}
|
|
||||||
public ValueEval evaluate(int srcRowIndex, int srcColumnIndex, ValueEval arg0, ValueEval arg1,
|
|
||||||
ValueEval arg2, ValueEval arg3) {
|
|
||||||
double result;
|
|
||||||
try {
|
|
||||||
double rate = NumericFunction.singleOperandEvaluate(arg0, srcRowIndex, srcColumnIndex);
|
|
||||||
double d1 = NumericFunction.singleOperandEvaluate(arg1, srcRowIndex, srcColumnIndex);
|
|
||||||
double d2 = NumericFunction.singleOperandEvaluate(arg2, srcRowIndex, srcColumnIndex);
|
|
||||||
double d3 = NumericFunction.singleOperandEvaluate(arg3, srcRowIndex, srcColumnIndex);
|
|
||||||
result = evaluate(rate, d1, d2, d3);
|
|
||||||
NumericFunction.checkValue(result);
|
|
||||||
} catch (EvaluationException e) {
|
|
||||||
return e.getErrorEval();
|
|
||||||
}
|
|
||||||
return new NumberEval(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
public ValueEval evaluate(ValueEval[] args, int srcRowIndex, int srcColumnIndex) {
|
||||||
int nArgs = args.length;
|
int nArgs = args.length;
|
||||||
if (nArgs<2) {
|
if (nArgs < 2) {
|
||||||
return ErrorEval.VALUE_INVALID;
|
return ErrorEval.VALUE_INVALID;
|
||||||
}
|
}
|
||||||
int np = nArgs-1;
|
|
||||||
double[] ds = new double[np];
|
|
||||||
double result;
|
|
||||||
try {
|
try {
|
||||||
double rate = NumericFunction.singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex);
|
double rate = NumericFunction.singleOperandEvaluate(args[0], srcRowIndex, srcColumnIndex);
|
||||||
for (int i = 0; i < ds.length; i++) {
|
// convert tail arguments into an array of doubles
|
||||||
ds[i] = NumericFunction.singleOperandEvaluate(args[i+1], srcRowIndex, srcColumnIndex);
|
ValueEval[] vargs = Arrays.copyOfRange(args, 1 , args.length);
|
||||||
}
|
double[] values = AggregateFunction.ValueCollector.collectValues(vargs);
|
||||||
result = evaluate(rate, ds);
|
|
||||||
|
double result = FinanceLib.npv(rate, values);
|
||||||
NumericFunction.checkValue(result);
|
NumericFunction.checkValue(result);
|
||||||
|
return new NumberEval(result);
|
||||||
} catch (EvaluationException e) {
|
} catch (EvaluationException e) {
|
||||||
return e.getErrorEval();
|
return e.getErrorEval();
|
||||||
}
|
}
|
||||||
return new NumberEval(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double evaluate(double rate, double...ds) {
|
|
||||||
double sum = 0;
|
|
||||||
for (int i = 0; i < ds.length; i++) {
|
|
||||||
sum += ds[i] / Math.pow(rate + 1, i);
|
|
||||||
}
|
|
||||||
return sum;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
128
src/testcases/org/apache/poi/ss/formula/functions/TestIrr.java
Normal file
128
src/testcases/org/apache/poi/ss/formula/functions/TestIrr.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.ss.formula.functions;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.framework.AssertionFailedError;
|
||||||
|
import org.apache.poi.hssf.usermodel.*;
|
||||||
|
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||||
|
import org.apache.poi.ss.usermodel.CellValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Irr}
|
||||||
|
*
|
||||||
|
* @author Marcel May
|
||||||
|
*/
|
||||||
|
public final class TestIrr extends TestCase {
|
||||||
|
|
||||||
|
public void testIrr() {
|
||||||
|
// http://en.wikipedia.org/wiki/Internal_rate_of_return#Example
|
||||||
|
double[] incomes = {-4000d, 1200d, 1410d, 1875d, 1050d};
|
||||||
|
double irr = Irr.irr(incomes);
|
||||||
|
double irrRounded = Math.round(irr * 1000d) / 1000d;
|
||||||
|
assertEquals("irr", 0.143d, irrRounded);
|
||||||
|
|
||||||
|
// http://www.techonthenet.com/excel/formulas/irr.php
|
||||||
|
incomes = new double[]{-7500d, 3000d, 5000d, 1200d, 4000d};
|
||||||
|
irr = Irr.irr(incomes);
|
||||||
|
irrRounded = Math.round(irr * 100d) / 100d;
|
||||||
|
assertEquals("irr", 0.28d, irrRounded);
|
||||||
|
|
||||||
|
incomes = new double[]{-10000d, 3400d, 6500d, 1000d};
|
||||||
|
irr = Irr.irr(incomes);
|
||||||
|
irrRounded = Math.round(irr * 100d) / 100d;
|
||||||
|
assertEquals("irr", 0.05, irrRounded);
|
||||||
|
|
||||||
|
incomes = new double[]{100d, -10d, -110d};
|
||||||
|
irr = Irr.irr(incomes);
|
||||||
|
irrRounded = Math.round(irr * 100d) / 100d;
|
||||||
|
assertEquals("irr", 0.1, irrRounded);
|
||||||
|
|
||||||
|
incomes = new double[]{-70000d, 12000, 15000};
|
||||||
|
irr = Irr.irr(incomes, -0.1);
|
||||||
|
irrRounded = Math.round(irr * 100d) / 100d;
|
||||||
|
assertEquals("irr", -0.44, irrRounded);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEvaluateInSheet() {
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFSheet sheet = wb.createSheet("Sheet1");
|
||||||
|
HSSFRow row = sheet.createRow(0);
|
||||||
|
|
||||||
|
row.createCell(0).setCellValue(-4000d);
|
||||||
|
row.createCell(1).setCellValue(1200d);
|
||||||
|
row.createCell(2).setCellValue(1410d);
|
||||||
|
row.createCell(3).setCellValue(1875d);
|
||||||
|
row.createCell(4).setCellValue(1050d);
|
||||||
|
|
||||||
|
HSSFCell cell = row.createCell(5);
|
||||||
|
cell.setCellFormula("IRR(A1:E1)");
|
||||||
|
|
||||||
|
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
|
||||||
|
fe.clearAllCachedResultValues();
|
||||||
|
fe.evaluateFormulaCell(cell);
|
||||||
|
double res = cell.getNumericCellValue();
|
||||||
|
assertEquals(0.143d, Math.round(res * 1000d) / 1000d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIrrFromSpreadsheet(){
|
||||||
|
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("IrrNpvTestCaseData.xls");
|
||||||
|
HSSFSheet sheet = wb.getSheet("IRR-NPV");
|
||||||
|
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
|
||||||
|
StringBuffer failures = new StringBuffer();
|
||||||
|
int failureCount = 0;
|
||||||
|
// TODO YK: Formulas in rows 16 and 17 operate with ArrayPtg which isn't yet supported
|
||||||
|
// FormulaEvaluator as of r1041407 throws "Unexpected ptg class (org.apache.poi.ss.formula.ptg.ArrayPtg)"
|
||||||
|
for(int rownum = 9; rownum <= 15; rownum++){
|
||||||
|
HSSFRow row = sheet.getRow(rownum);
|
||||||
|
HSSFCell cellA = row.getCell(0);
|
||||||
|
try {
|
||||||
|
CellValue cv = fe.evaluate(cellA);
|
||||||
|
assertFormulaResult(cv, cellA);
|
||||||
|
} catch (Throwable e){
|
||||||
|
if(failures.length() > 0) failures.append('\n');
|
||||||
|
failures.append("Row[" + (cellA.getRowIndex() + 1)+ "]: " + cellA.getCellFormula() + " ");
|
||||||
|
failures.append(e.getMessage());
|
||||||
|
failureCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
HSSFCell cellC = row.getCell(2); //IRR-NPV relationship: NPV(IRR(values), values) = 0
|
||||||
|
try {
|
||||||
|
CellValue cv = fe.evaluate(cellC);
|
||||||
|
assertEquals(0, cv.getNumberValue(), 0.0001); // should agree within 0.01%
|
||||||
|
} catch (Throwable e){
|
||||||
|
if(failures.length() > 0) failures.append('\n');
|
||||||
|
failures.append("Row[" + (cellC.getRowIndex() + 1)+ "]: " + cellC.getCellFormula() + " ");
|
||||||
|
failures.append(e.getMessage());
|
||||||
|
failureCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(failures.length() > 0) {
|
||||||
|
throw new AssertionFailedError(failureCount + " IRR assertions failed:\n" + failures.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertFormulaResult(CellValue cv, HSSFCell cell){
|
||||||
|
double actualValue = cv.getNumberValue();
|
||||||
|
double expectedValue = cell.getNumericCellValue(); // cached formula result calculated by Excel
|
||||||
|
assertEquals("Invalid formula result: " + cv.toString(), HSSFCell.CELL_TYPE_NUMERIC, cv.getCellType());
|
||||||
|
assertEquals(expectedValue, actualValue, 1E-4); // should agree within 0.01%
|
||||||
|
}
|
||||||
|
}
|
104
src/testcases/org/apache/poi/ss/formula/functions/TestNpv.java
Normal file
104
src/testcases/org/apache/poi/ss/formula/functions/TestNpv.java
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/* ====================================================================
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
contributor license agreements. See the NOTICE file distributed with
|
||||||
|
this work for additional information regarding copyright ownership.
|
||||||
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
(the "License"); you may not use this file except in compliance with
|
||||||
|
the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
==================================================================== */
|
||||||
|
|
||||||
|
package org.apache.poi.ss.formula.functions;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import junit.framework.AssertionFailedError;
|
||||||
|
import org.apache.poi.hssf.usermodel.*;
|
||||||
|
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||||
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
|
import org.apache.poi.ss.usermodel.Row;
|
||||||
|
import org.apache.poi.ss.usermodel.CellValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link Npv}
|
||||||
|
*
|
||||||
|
* @author Marcel May
|
||||||
|
* @see <a href="http://office.microsoft.com/en-us/excel-help/npv-HP005209199.aspx">Excel Help</a>
|
||||||
|
*/
|
||||||
|
public final class TestNpv extends TestCase {
|
||||||
|
|
||||||
|
public void testEvaluateInSheetExample2() {
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFSheet sheet = wb.createSheet("Sheet1");
|
||||||
|
HSSFRow row = sheet.createRow(0);
|
||||||
|
|
||||||
|
sheet.createRow(1).createCell(0).setCellValue(0.08d);
|
||||||
|
sheet.createRow(2).createCell(0).setCellValue(-40000d);
|
||||||
|
sheet.createRow(3).createCell(0).setCellValue(8000d);
|
||||||
|
sheet.createRow(4).createCell(0).setCellValue(9200d);
|
||||||
|
sheet.createRow(5).createCell(0).setCellValue(10000d);
|
||||||
|
sheet.createRow(6).createCell(0).setCellValue(12000d);
|
||||||
|
sheet.createRow(7).createCell(0).setCellValue(14500d);
|
||||||
|
|
||||||
|
HSSFCell cell = row.createCell(8);
|
||||||
|
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
|
||||||
|
|
||||||
|
// Enumeration
|
||||||
|
cell.setCellFormula("NPV(A2, A4,A5,A6,A7,A8)+A3");
|
||||||
|
fe.clearAllCachedResultValues();
|
||||||
|
fe.evaluateFormulaCell(cell);
|
||||||
|
double res = cell.getNumericCellValue();
|
||||||
|
assertEquals(1922.06d, Math.round(res * 100d) / 100d);
|
||||||
|
|
||||||
|
// Range
|
||||||
|
cell.setCellFormula("NPV(A2, A4:A8)+A3");
|
||||||
|
|
||||||
|
fe.clearAllCachedResultValues();
|
||||||
|
fe.evaluateFormulaCell(cell);
|
||||||
|
res = cell.getNumericCellValue();
|
||||||
|
assertEquals(1922.06d, Math.round(res * 100d) / 100d);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* evaluate formulas with NPV and compare the result with
|
||||||
|
* the cached formula result pre-calculated by Excel
|
||||||
|
*/
|
||||||
|
public void testNpvFromSpreadsheet(){
|
||||||
|
HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook("IrrNpvTestCaseData.xls");
|
||||||
|
HSSFSheet sheet = wb.getSheet("IRR-NPV");
|
||||||
|
HSSFFormulaEvaluator fe = new HSSFFormulaEvaluator(wb);
|
||||||
|
StringBuffer failures = new StringBuffer();
|
||||||
|
int failureCount = 0;
|
||||||
|
// TODO YK: Formulas in rows 16 and 17 operate with ArrayPtg which isn't yet supported
|
||||||
|
// FormulaEvaluator as of r1041407 throws "Unexpected ptg class (org.apache.poi.ss.formula.ptg.ArrayPtg)"
|
||||||
|
for(int rownum = 9; rownum <= 15; rownum++){
|
||||||
|
HSSFRow row = sheet.getRow(rownum);
|
||||||
|
HSSFCell cellB = row.getCell(1);
|
||||||
|
try {
|
||||||
|
CellValue cv = fe.evaluate(cellB);
|
||||||
|
assertFormulaResult(cv, cellB);
|
||||||
|
} catch (Throwable e){
|
||||||
|
if(failures.length() > 0) failures.append('\n');
|
||||||
|
failures.append("Row[" + (cellB.getRowIndex() + 1)+ "]: " + cellB.getCellFormula() + " ");
|
||||||
|
failures.append(e.getMessage());
|
||||||
|
failureCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(failures.length() > 0) {
|
||||||
|
throw new AssertionFailedError(failureCount + " IRR evaluations failed:\n" + failures.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertFormulaResult(CellValue cv, HSSFCell cell){
|
||||||
|
double actualValue = cv.getNumberValue();
|
||||||
|
double expectedValue = cell.getNumericCellValue(); // cached formula result calculated by Excel
|
||||||
|
assertEquals(expectedValue, actualValue, 1E-4); // should agree within 0.01%
|
||||||
|
}
|
||||||
|
}
|
BIN
test-data/spreadsheet/IrrNpvTestCaseData.xls
Normal file
BIN
test-data/spreadsheet/IrrNpvTestCaseData.xls
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user