Ваши наблюдения верны. Проблема возникает из-за общих проблем с плавающей точкой. Можно просто показать:
...
System.out.println(1.7/20.0); //0.08499999999999999
System.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
FormulaEvaluator
, ни DataFormatter
не ведут себя как Excel
в этом пункте. Вот почему разница.
Можно иметь собственный MyDataFormatter
, где единственное отличие от / org / apache / poi / ss / usermodel / DataFormatter.java :
...
private String getFormattedNumberString(Cell cell, ConditionalFormattingEvaluator cfEvaluator) {
if (cell == null) {
return null;
}
Format numberFormat = getFormat(cell, cfEvaluator);
double d = cell.getNumericCellValue();
java.math.BigDecimal bd = new java.math.BigDecimal(d);
d = bd.round(new java.math.MathContext(15)).doubleValue();
if (numberFormat == null) {
return String.valueOf(d);
}
String formatted = numberFormat.format(Double.valueOf(d));
return formatted.replaceFirst("E(\\d)", "E+$1"); // to match Excel's E-notation
}
...
Тогда использование этого MyDataFormatter
вместо 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 {
public static void main(String[] args) throws Exception {
Workbook workbook = new XSSFWorkbook();
CreationHelper creationHelper = workbook.getCreationHelper();
FormulaEvaluator formulaEvaluator = creationHelper.createFormulaEvaluator();
Sheet sheet = workbook.createSheet();
Row row = sheet.createRow(0);
Cell cell = row.createCell(0); cell.setCellValue(21.7);
cell = row.createCell(1); cell.setCellValue(20.0);
cell = row.createCell(2); cell.setCellFormula("(A1-B1)/B1");
formulaEvaluator.evaluateFormulaCell(cell);
double d = cell.getNumericCellValue();
System.out.println(d); //0.08499999999999996
MyDataFormatter dataFormatter = new MyDataFormatter();
String myFormat="0%";
CellUtil.setCellStyleProperty(cell, CellUtil.DATA_FORMAT, creationHelper.createDataFormat().getFormat(myFormat));
String val = dataFormatter.formatCellValue(cell, formulaEvaluator);
System.out.println(val); //9%
FileOutputStream out = new FileOutputStream("Excel.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}