猿问

Java POI FormulaEvaluator 给出意外的浮点值

这是《Java 哲学》一书中的示例(请记住 FancyToy 扩展了 Toy):


Class<FancyToy> ftClass = FancyToy.class;

Class<? super FancyToy> up = ftClass.getSuperclass(); //compiles

Class<Toy> up = ftClass.getSuperclass(); //doesn't compile 

在IDEA中尝试了很多次,只是出现“不兼容类型”的消息。请给我一个提示,为什么我们不能仅使用泛型类型玩具?我正在使用 Java POI 库读取 Excel 文件,然后将其显示在 HTML 表格中。Excel 文件非常简单,只有 1 行 3 列:


A1 单元格= 21.7 B1 单元格= 20.0 C1 单元格是一个公式单元格,其公式为 =(A1-B1)/B1,它具有自定义格式“百分比”,小数点后 0 位。Excel 将其值显示为 9%。这是因为 1.7/20 在计算器上给出的结果是 0.085;当它转换为“百分比”格式时,它会变成 8.5%,并且因为格式显示包含 0 位小数,所以它会四舍五入到 9%,这就是 Excel 显示的内容。都好。


然而,POI 将该值显示为 8%。我观察到 1.7/20 的计算结果是 0.084999999。由于上面应用的百分比格式,它会转换为 8.4999999%,并且由于小数点后 0 位,它会向下舍入为 8%。


如何让 POI 返还 9% 而不是 8%?这是代码片段:


String myFormat="0%";

CreationHelper helper = wbWrapper.getWb().getCreationHelper();

CellUtil.setCellStyleProperty(cell, CellUtil.DATA_FORMAT,helper.createDataFormat().getFormat(myFormat));

String val = dataFormatter.formatCellValue(cell, evaluator);

这里 evaluator 是 org.apache.poi.ss.usermodel.FormulaEvaluator 的实例,dataFormatter 是 org.apache.poi.ss.usermodel.DataFormatter 的实例


当我打印变量“val”时,它返回 8%,而不是 Excel 中显示的值 (9%)。


桃花长相依
浏览 169回答 1
1回答

茅侃侃

你的观察是正确的。该问题的出现是因为一般的浮点问题。可以简单地表示为:...System.out.println(1.7/20.0); //0.08499999999999999System.out.println((21.7-20.0)/20.0); //0.08499999999999996...如您所见,双精度值 1.7 除以双精度值 20.0 得到 0.08499999999999999。这很好,因为使用 时该值将被视为 0.085 DecimalFormat。但更复杂的方程 (21.7-20.0)/20.0 结果为 0.08499999999999996。这显然低于 0.085。Excel尝试通过浮点值的附加规则来解决这些问题。它始终仅使用浮点值的 15 位有效十进制数字。类似的事情也是如此Excel:...BigDecimal bd = new BigDecimal((21.7-20.0)/20.0);System.out.println(bd.round(new MathContext(15)).doubleValue()); //0.085...apache poi's FormulaEvaluator和 it's 的DataFormatter行为都不像这Excel一点。这就是差异的原因。人们可以拥有一个自己的,与/org/apache/poi/ss/usermodel/DataFormatter.javaMyDataFormatter的唯一区别是:...&nbsp; &nbsp; private String getFormattedNumberString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) {&nbsp; &nbsp; &nbsp; &nbsp; if (cell == null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; Format numberFormat = getFormat(cell, cfEvaluator);&nbsp; &nbsp; &nbsp; &nbsp; double d = cell.getNumericCellValue();&nbsp; &nbsp; &nbsp; &nbsp; java.math.BigDecimal bd = new java.math.BigDecimal(d);&nbsp; &nbsp; &nbsp; &nbsp; d = bd.round(new java.math.MathContext(15)).doubleValue();&nbsp; &nbsp; &nbsp; &nbsp; if (numberFormat == null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return String.valueOf(d);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; String formatted = numberFormat.format(Double.valueOf(d));&nbsp; &nbsp; &nbsp; &nbsp; return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation&nbsp; &nbsp; }...然后使用 thatMyDataFormatter而不是DataFormatter会更符合 的Excel行为。例子:import java.io.FileOutputStream;import org.apache.poi.ss.usermodel.*;import org.apache.poi.ss.util.*;import org.apache.poi.xssf.usermodel.XSSFWorkbook;class CreateExcelEvaluateFormula {&nbsp;public static void main(String[] args) throws Exception {&nbsp; Workbook workbook&nbsp; = new XSSFWorkbook();&nbsp; CreationHelper creationHelper = workbook.getCreationHelper();&nbsp; FormulaEvaluator formulaEvaluator = creationHelper.createFormulaEvaluator();&nbsp; Sheet sheet = workbook.createSheet();&nbsp; Row row = sheet.createRow(0);&nbsp; Cell cell = row.createCell(0); cell.setCellValue(21.7);&nbsp; cell = row.createCell(1); cell.setCellValue(20.0);&nbsp; cell = row.createCell(2); cell.setCellFormula("(A1-B1)/B1");&nbsp; formulaEvaluator.evaluateFormulaCell(cell);&nbsp;&nbsp; double d = cell.getNumericCellValue();System.out.println(d); //0.08499999999999996&nbsp; MyDataFormatter dataFormatter = new MyDataFormatter();&nbsp; String myFormat="0%";&nbsp; CellUtil.setCellStyleProperty(cell, CellUtil.DATA_FORMAT, creationHelper.createDataFormat().getFormat(myFormat));&nbsp; String val = dataFormatter.formatCellValue(cell, formulaEvaluator);&nbsp; System.out.println(val); //9%&nbsp; FileOutputStream out = new FileOutputStream("Excel.xlsx");&nbsp; workbook.write(out);&nbsp; out.close();&nbsp; workbook.close();&nbsp;}}
随时随地看视频慕课网APP

相关分类

Java
我要回答