poi框架导出excel写单元格遇到精度问题

背景:

java系统,MySql数据库,定义有些数据格式为Decimal(24,2),即最多整数22位,小数2位,或者Decimal(24,4),即最多整数20位,小数4位的数字。系统内部操作使用BigDecimal来记录和操作这样的数据,并无不妥,也不会丢失数据,但是当要将这样的数据导出的excel,问题出现了,为了便于用户使用excel对数据(可能是金额,数量)作分析,所以我们保证单元格写的还是数字。
以金额为例,虽然使用了formater:”#,##0.00”来格式化数字,使之显示为形如”52,441,314.20”这个样的字符串,但这个单元格的内部value还是”52441314.20”,还是数字。
系统中,调用poi框架写这样的一个数字的代码如下:

cell = row.createCell(idx, CellType.NUMERIC);
cell.setCellValue(((BigDecimal) val).doubleValue());
cell.setCellStyle(cellStyles.getMoneyStyle());

第一行创建一个数字格式的单元格。
第二行是将系统内部的数据写到单元格中去。
第三行是设置单元格的格式引用(多说一句,一个excel对一种单元格格式尽量使用同一个单元格格式变量,即尽量复用单元格格式,因为存放单元格格式是需要代价的)。
我们所使用的POI3.15仅支持下面一些setValue的方式,毫无疑问,这里只能先将BigDecimal转换成double再存放进去。
然后,问题开始出现了,当要写入的val值出现形如”5244524452445244.13”这样的大数字(18,2),在系统允许范围内,内部计算使用BigDecimal也没问题。但是当想要导出到excel的时候,问题出现了,被写入的数据是:
5.244524452445240E15
明显的丢失了精度。经过简单排查,很快发现了问题就是出现在了BigDecinal.doubleValue()这个方法上面。

BigDecimal 5244524452445244.13
Double 5.244524452445244E15

正是Double的值成了科学计数法提醒了我,虽然double类型可以具有宽阔的表值空间:-1.7*10(-308)~1.7*10(308)。但是它能维持的精度可能没有这里需要的高。

double结构分析:

看一下double的格式,一个double数是64位,它分为三个区域:

1 11 52
符号位 指数位 尾数位

简单举一个例子,十进制数字9二进制是:1001,
十进制数字0.625的二进制是:0.101;
所以十进制数字9.625的十进制数是1001.101,也就是1.001101*2^3。double是移位存储(这个概念不清楚请自行百度)的,所以这个数字存放进double就是:

符号位 指数位 尾数位
0 1…100 0011010…0
63 62…52 51…0

因为移位计算后,科学计数法首位都是1,不用存,所以double 52位的尾数能表达出53位的精度。

范围

11bit的指数位的表达区间是-2^10到2^10
所以double的表值范围是:
2(210)

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值