在使用 EasyExcel 读取 Excel 时,我们经常会遇到这样一个问题:
同一个单元格,Excel 界面显示 119178409,但在公式栏里显示的原始值却是 119178408.985065。

而通过 EasyExcel 读取后,拿到的值始终是 119178409,即 Excel 的“显示值”,而不是底层存储的原始值。
这在财务、对账、数据精度要求较高的场景里,往往会造成严重的数据误差。
Excel 单元格内容:
119178409119178408.985065使用 EasyExcel 默认读取方式:
EasyExcel.read(fileBytes)
.registerReadListener(new ReadListener<Map<Integer, String>>() {
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
System.out.println(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
}).sheet().doRead();
输出结果:
{0=119178409}
可以看到,原始的 119178408.985065 被四舍五入成了 119178409。
EasyExcel 的默认行为是:
Map<Integer, String>,使用内置的 StringConverter。StringConverter 内部依赖 Apache POI 的 DataFormatter。DataFormatter 返回的就是 Excel UI 显示值,而不是单元格底层的存储值。所以,只要你用 Map<Integer, String> 或者 Java Bean 的 String 字段接收,就一定会丢失原始精度。
Map<Integer, CellData>(推荐)直接拿 CellData,通过 getNumberValue() 获取 Excel 的原始值:
EasyExcel.read(fileBytes)
.registerReadListener(new ReadListener<Map<Integer, CellData>>() {
@Override
public void invoke(Map<Integer, CellData> data, AnalysisContext context) {
for (Map.Entry<Integer, CellData> entry : data.entrySet()) {
CellData cellData = entry.getValue();
if (cellData.getType() == CellDataTypeEnum.NUMBER) {
System.out.println("原始值 = " + cellData.getNumberValue().toPlainString());
}
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
}).sheet().doRead();
输出结果:
原始值 = 119178408.985065
如果你必须用 Map<Integer, String>,可以自定义一个 RawNumberStringConverter:
public class RawNumberStringConverter implements Converter<String> {
@Override
public Class<?> supportJavaTypeKey() {
return String.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.NUMBER;
}
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return cellData.getNumberValue().toPlainString();
}
}
注册时一定要放在前面,覆盖默认的:
EasyExcel.read(fileBytes)
.registerConverter(new RawNumberStringConverter())
.registerReadListener(new ReadListener<Map<Integer, String>>() {
@Override
public void invoke(Map<Integer, String> data, AnalysisContext context) {
System.out.println(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
}).sheet().doRead();
这样 data 中拿到的就是 119178408.985065。
Map<Integer, CellData>,直接取 getNumberValue();Converter 覆盖默认的 StringConverter。⚠️ 经验教训:EasyExcel 在默认场景下非常方便,但在高精度数值处理场景里,一定要检查它是否返回了 原始值,否则可能踩坑。