mirror of https://github.com/apache/poi.git
Added implementation for MROUND(), VAR() and VARP()
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1044642 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
544463463a
commit
72ce4e8eb7
|
@ -34,6 +34,7 @@
|
||||||
|
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.8-beta1" date="2010-??-??">
|
<release version="3.8-beta1" date="2010-??-??">
|
||||||
|
<action dev="POI-DEVELOPERS" type="add">48539 - Added implementation for MROUND(), VAR() and VARP()</action>
|
||||||
<action dev="POI-DEVELOPERS" type="add">50446 - Code cleanup and optimizations to keep some IDE quiet</action>
|
<action dev="POI-DEVELOPERS" type="add">50446 - Code cleanup and optimizations to keep some IDE quiet</action>
|
||||||
<action dev="POI-DEVELOPERS" type="add">50437 - Support passing ranges to NPV()</action>
|
<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">50409 - Added implementation for IRR()</action>
|
||||||
|
|
|
@ -138,7 +138,7 @@ public final class AnalysisToolPak implements UDFFinder {
|
||||||
r(m, "JIS", null);
|
r(m, "JIS", null);
|
||||||
r(m, "LCM", null);
|
r(m, "LCM", null);
|
||||||
r(m, "MDURATION", null);
|
r(m, "MDURATION", null);
|
||||||
r(m, "MROUND", null);
|
r(m, "MROUND", MRound.instance);
|
||||||
r(m, "MULTINOMIAL", null);
|
r(m, "MULTINOMIAL", null);
|
||||||
r(m, "NETWORKDAYS", null);
|
r(m, "NETWORKDAYS", null);
|
||||||
r(m, "NOMINAL", null);
|
r(m, "NOMINAL", null);
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* ====================================================================
|
||||||
|
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.atp;
|
||||||
|
|
||||||
|
import org.apache.poi.ss.formula.OperationEvaluationContext;
|
||||||
|
import org.apache.poi.ss.formula.eval.*;
|
||||||
|
import org.apache.poi.ss.formula.functions.FreeRefFunction;
|
||||||
|
import org.apache.poi.ss.formula.functions.NumericFunction;
|
||||||
|
import org.apache.poi.ss.usermodel.DateUtil;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of Excel 'Analysis ToolPak' function MROUND()<br/>
|
||||||
|
*
|
||||||
|
* Returns a number rounded to the desired multiple.<p/>
|
||||||
|
*
|
||||||
|
* <b>Syntax</b><br/>
|
||||||
|
* <b>MROUND</b>(<b>number</b>, <b>multiple</b>)
|
||||||
|
*
|
||||||
|
* <p/>
|
||||||
|
*
|
||||||
|
* @author Yegor Kozlov
|
||||||
|
*/
|
||||||
|
final class MRound implements FreeRefFunction {
|
||||||
|
|
||||||
|
public static final FreeRefFunction instance = new MRound();
|
||||||
|
|
||||||
|
private MRound() {
|
||||||
|
// enforce singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueEval evaluate(ValueEval[] args, OperationEvaluationContext ec) {
|
||||||
|
double number, multiple, result;
|
||||||
|
|
||||||
|
if (args.length != 2) {
|
||||||
|
return ErrorEval.VALUE_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
number = OperandResolver.coerceValueToDouble(OperandResolver.getSingleValue(args[0], ec.getRowIndex(), ec.getColumnIndex()));
|
||||||
|
multiple = OperandResolver.coerceValueToDouble(OperandResolver.getSingleValue(args[1], ec.getRowIndex(), ec.getColumnIndex()));
|
||||||
|
|
||||||
|
if( multiple == 0.0 ) {
|
||||||
|
result = 0.0;
|
||||||
|
} else {
|
||||||
|
if(number*multiple < 0) {
|
||||||
|
// Returns #NUM! because the number and the multiple have different signs
|
||||||
|
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||||
|
}
|
||||||
|
result = multiple * Math.round( number / multiple );
|
||||||
|
}
|
||||||
|
NumericFunction.checkValue(result);
|
||||||
|
return new NumberEval(result);
|
||||||
|
} catch (EvaluationException e) {
|
||||||
|
return e.getErrorEval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -93,6 +93,8 @@ public final class FunctionEval {
|
||||||
retval[37] = BooleanFunction.OR;
|
retval[37] = BooleanFunction.OR;
|
||||||
retval[38] = BooleanFunction.NOT;
|
retval[38] = BooleanFunction.NOT;
|
||||||
retval[39] = NumericFunction.MOD;
|
retval[39] = NumericFunction.MOD;
|
||||||
|
|
||||||
|
retval[46] = AggregateFunction.VAR;
|
||||||
retval[48] = TextFunction.TEXT;
|
retval[48] = TextFunction.TEXT;
|
||||||
|
|
||||||
retval[56] = FinanceFunction.PV;
|
retval[56] = FinanceFunction.PV;
|
||||||
|
@ -153,6 +155,7 @@ public final class FunctionEval {
|
||||||
retval[184] = NumericFunction.FACT;
|
retval[184] = NumericFunction.FACT;
|
||||||
|
|
||||||
retval[190] = LogicalFunction.ISNONTEXT;
|
retval[190] = LogicalFunction.ISNONTEXT;
|
||||||
|
retval[194] = AggregateFunction.VARP;
|
||||||
retval[197] = NumericFunction.TRUNC;
|
retval[197] = NumericFunction.TRUNC;
|
||||||
retval[198] = LogicalFunction.ISLOGICAL;
|
retval[198] = LogicalFunction.ISLOGICAL;
|
||||||
|
|
||||||
|
|
|
@ -142,4 +142,20 @@ public abstract class AggregateFunction extends MultiOperandNumericFunction {
|
||||||
return MathX.sumsq(values);
|
return MathX.sumsq(values);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
public static final Function VAR = new AggregateFunction() {
|
||||||
|
protected double evaluate(double[] values) throws EvaluationException {
|
||||||
|
if (values.length < 1) {
|
||||||
|
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||||
|
}
|
||||||
|
return StatsLib.var(values);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
public static final Function VARP = new AggregateFunction() {
|
||||||
|
protected double evaluate(double[] values) throws EvaluationException {
|
||||||
|
if (values.length < 1) {
|
||||||
|
throw new EvaluationException(ErrorEval.DIV_ZERO);
|
||||||
|
}
|
||||||
|
return StatsLib.varp(values);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ public abstract class NumericFunction implements Function {
|
||||||
/**
|
/**
|
||||||
* @throws EvaluationException (#NUM!) if <tt>result</tt> is <tt>NaN</> or <tt>Infinity</tt>
|
* @throws EvaluationException (#NUM!) if <tt>result</tt> is <tt>NaN</> or <tt>Infinity</tt>
|
||||||
*/
|
*/
|
||||||
static final void checkValue(double result) throws EvaluationException {
|
public static final void checkValue(double result) throws EvaluationException {
|
||||||
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
||||||
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
throw new EvaluationException(ErrorEval.NUM_ERROR);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,21 @@ final class StatsLib {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double var(double[] v) {
|
||||||
|
double r = Double.NaN;
|
||||||
|
if (v!=null && v.length > 1) {
|
||||||
|
r = devsq(v) / (v.length - 1);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double varp(double[] v) {
|
||||||
|
double r = Double.NaN;
|
||||||
|
if (v!=null && v.length > 1) {
|
||||||
|
r = devsq(v) /v.length;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
public static double median(double[] v) {
|
public static double median(double[] v) {
|
||||||
double r = Double.NaN;
|
double r = Double.NaN;
|
||||||
|
|
|
@ -26,16 +26,8 @@ import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||||
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
|
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
|
||||||
import org.apache.poi.ss.usermodel.BaseTestBugzillaIssues;
|
import org.apache.poi.ss.usermodel.*;
|
||||||
import org.apache.poi.ss.usermodel.Cell;
|
import org.apache.poi.ss.formula.eval.NotImplementedException;
|
||||||
import org.apache.poi.ss.usermodel.DataFormatter;
|
|
||||||
import org.apache.poi.ss.usermodel.Font;
|
|
||||||
import org.apache.poi.ss.usermodel.FormulaError;
|
|
||||||
import org.apache.poi.ss.usermodel.FormulaEvaluator;
|
|
||||||
import org.apache.poi.ss.usermodel.Name;
|
|
||||||
import org.apache.poi.ss.usermodel.Row;
|
|
||||||
import org.apache.poi.ss.usermodel.Sheet;
|
|
||||||
import org.apache.poi.ss.usermodel.Workbook;
|
|
||||||
import org.apache.poi.xssf.XSSFITestDataProvider;
|
import org.apache.poi.xssf.XSSFITestDataProvider;
|
||||||
import org.apache.poi.xssf.XSSFTestDataSamples;
|
import org.apache.poi.xssf.XSSFTestDataSamples;
|
||||||
import org.apache.poi.xssf.model.CalculationChain;
|
import org.apache.poi.xssf.model.CalculationChain;
|
||||||
|
@ -212,8 +204,10 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
|
||||||
* NameXPtgs.
|
* NameXPtgs.
|
||||||
* Blows up on:
|
* Blows up on:
|
||||||
* IF(B6= (ROUNDUP(B6,0) + ROUNDDOWN(B6,0))/2, MROUND(B6,2),ROUND(B6,0))
|
* IF(B6= (ROUNDUP(B6,0) + ROUNDDOWN(B6,0))/2, MROUND(B6,2),ROUND(B6,0))
|
||||||
|
*
|
||||||
|
* TODO: delete this test case when MROUND and VAR are implemented
|
||||||
*/
|
*/
|
||||||
public void DISABLEDtest48539() throws Exception {
|
public void test48539() throws Exception {
|
||||||
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("48539.xlsx");
|
XSSFWorkbook wb = XSSFTestDataSamples.openSampleWorkbook("48539.xlsx");
|
||||||
assertEquals(3, wb.getNumberOfSheets());
|
assertEquals(3, wb.getNumberOfSheets());
|
||||||
|
|
||||||
|
@ -224,14 +218,21 @@ public final class TestXSSFBugs extends BaseTestBugzillaIssues {
|
||||||
for(Row r : s) {
|
for(Row r : s) {
|
||||||
for(Cell c : r) {
|
for(Cell c : r) {
|
||||||
if(c.getCellType() == Cell.CELL_TYPE_FORMULA) {
|
if(c.getCellType() == Cell.CELL_TYPE_FORMULA) {
|
||||||
eval.evaluate(c);
|
CellValue cv = eval.evaluate(c);
|
||||||
|
if(cv.getCellType() == Cell.CELL_TYPE_NUMERIC) {
|
||||||
|
// assert that the calculated value agrees with
|
||||||
|
// the cached formula result calculated by Excel
|
||||||
|
double cachedFormulaResult = c.getNumericCellValue();
|
||||||
|
double evaluatedFormulaResult = cv.getNumberValue();
|
||||||
|
assertEquals(c.getCellFormula(), cachedFormulaResult, evaluatedFormulaResult, 1E-7);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now all of them
|
// Now all of them
|
||||||
XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
|
XSSFFormulaEvaluator.evaluateAllFormulaCells(wb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* ====================================================================
|
||||||
|
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.atp;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.apache.poi.hssf.HSSFTestDataSamples;
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||||
|
import org.apache.poi.ss.usermodel.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testcase for 'Analysis Toolpak' function MROUND()
|
||||||
|
*
|
||||||
|
* @author Yegor Kozlov
|
||||||
|
*/
|
||||||
|
public class TestMRound extends TestCase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
=MROUND(10, 3) Rounds 10 to a nearest multiple of 3 (9)
|
||||||
|
=MROUND(-10, -3) Rounds -10 to a nearest multiple of -3 (-9)
|
||||||
|
=MROUND(1.3, 0.2) Rounds 1.3 to a nearest multiple of 0.2 (1.4)
|
||||||
|
=MROUND(5, -2) Returns an error, because -2 and 5 have different signs (#NUM!) *
|
||||||
|
*/
|
||||||
|
public static void testEvaluate(){
|
||||||
|
Workbook wb = new HSSFWorkbook();
|
||||||
|
Sheet sh = wb.createSheet();
|
||||||
|
Cell cell1 = sh.createRow(0).createCell(0);
|
||||||
|
cell1.setCellFormula("MROUND(10, 3)");
|
||||||
|
Cell cell2 = sh.createRow(0).createCell(0);
|
||||||
|
cell2.setCellFormula("MROUND(-10, -3)");
|
||||||
|
Cell cell3 = sh.createRow(0).createCell(0);
|
||||||
|
cell3.setCellFormula("MROUND(1.3, 0.2)");
|
||||||
|
Cell cell4 = sh.createRow(0).createCell(0);
|
||||||
|
cell4.setCellFormula("MROUND(5, -2)");
|
||||||
|
Cell cell5 = sh.createRow(0).createCell(0);
|
||||||
|
cell5.setCellFormula("MROUND(5, 0)");
|
||||||
|
|
||||||
|
double accuracy = 1E-9;
|
||||||
|
|
||||||
|
FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
|
||||||
|
|
||||||
|
assertEquals("Rounds 10 to a nearest multiple of 3 (9)",
|
||||||
|
9.0, evaluator.evaluate(cell1).getNumberValue(), accuracy);
|
||||||
|
|
||||||
|
assertEquals("Rounds -10 to a nearest multiple of -3 (-9)",
|
||||||
|
-9.0, evaluator.evaluate(cell2).getNumberValue(), accuracy);
|
||||||
|
|
||||||
|
assertEquals("Rounds 1.3 to a nearest multiple of 0.2 (1.4)",
|
||||||
|
1.4, evaluator.evaluate(cell3).getNumberValue(), accuracy);
|
||||||
|
|
||||||
|
assertEquals("Returns an error, because -2 and 5 have different signs (#NUM!)",
|
||||||
|
ErrorEval.NUM_ERROR.getErrorCode(), evaluator.evaluate(cell4).getErrorValue());
|
||||||
|
|
||||||
|
assertEquals("Returns 0 because the multiple is 0",
|
||||||
|
0.0, evaluator.evaluate(cell5).getNumberValue());
|
||||||
|
}
|
||||||
|
}
|
|
@ -268,4 +268,54 @@ public class TestStatsLib extends AbstractNumericTestCase {
|
||||||
x = 3.02765035410;
|
x = 3.02765035410;
|
||||||
assertEquals("stdev ", x, d);
|
assertEquals("stdev ", x, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testVar() {
|
||||||
|
double[] v = null;
|
||||||
|
double d, x = 0;
|
||||||
|
|
||||||
|
v = new double[] {3.50, 5.00, 7.23, 2.99};
|
||||||
|
d = StatsLib.var(v);
|
||||||
|
x = 3.6178;
|
||||||
|
assertEquals("var ", x, d);
|
||||||
|
|
||||||
|
v = new double[] {34.5, 2.0, 8.9, -4.0};
|
||||||
|
d = StatsLib.var(v);
|
||||||
|
x = 286.99;
|
||||||
|
assertEquals("var ", x, d);
|
||||||
|
|
||||||
|
v = new double[] {7.0, 25.0, 21.69};
|
||||||
|
d = StatsLib.var(v);
|
||||||
|
x = 91.79203333;
|
||||||
|
assertEquals("var ", x, d);
|
||||||
|
|
||||||
|
v = new double[] {1345,1301,1368,1322,1310,1370,1318,1350,1303,1299};
|
||||||
|
d = StatsLib.var(v);
|
||||||
|
x = 754.2666667;
|
||||||
|
assertEquals("var ", x, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testVarp() {
|
||||||
|
double[] v = null;
|
||||||
|
double d, x = 0;
|
||||||
|
|
||||||
|
v = new double[] {3.50, 5.00, 7.23, 2.99};
|
||||||
|
d = StatsLib.varp(v);
|
||||||
|
x = 2.71335;
|
||||||
|
assertEquals("varp ", x, d);
|
||||||
|
|
||||||
|
v = new double[] {34.5, 2.0, 8.9, -4.0};
|
||||||
|
d = StatsLib.varp(v);
|
||||||
|
x = 215.2425;
|
||||||
|
assertEquals("varp ", x, d);
|
||||||
|
|
||||||
|
v = new double[] {7.0, 25.0, 21.69};
|
||||||
|
d = StatsLib.varp(v);
|
||||||
|
x = 61.19468889;
|
||||||
|
assertEquals("varp ", x, d);
|
||||||
|
|
||||||
|
v = new double[] {1345,1301,1368,1322,1310,1370,1318,1350,1303,1299};
|
||||||
|
d = StatsLib.varp(v);
|
||||||
|
x = 678.84;
|
||||||
|
assertEquals("varp ", x, d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue