数字迷惑行为

dianx / 2023-08-05 / 原文

最近做处理金额的业务比较多,所以最近遇到两个关于数字的坑,记下来以便以后不犯这种错误

1.0.57*100=?

问题代码:
 

相信大多数人阅读上面这段代码都认为结果是57,但是运行结果却是56
问题原因:
计算机存储数字采用的是二进制,0.57在计算机中存储的二进制再转化成十进制的值是0.56999…,再乘100取整就是56。
解决办法:
 

先转化成String类型再用BigDecimal进行运算。BigDecimal中有记录有效数字长度的precision、记录小数点位置的scale以及有效数字intCompact。如果不转化成String,BigDecimal不会记录小数位和长度这些信息,计算时还是会像第一次那样进行数字的普通计算
 

2.FastJson处理double类型

问题代码:
 

其实结果倒是没错,但是问题在于一个使用科学技术法一个没有使用,这如果传给前端,客户肯定会反馈数字风格不一致的问题
问题原因:
使用fastjson将json字符串反序列化成Map对象时,如果属性值是double类型,那么fastjson会默认当成BigDecimal来处理,相当于fastjson会调用new BigDecimal(String.valueOf(d1).toCharArray(), 0, String.valueOf(d1).length(), MathContext.UNLIMITED);来创建一个BigDecimal对象,然后println方法会默认调用对象的toString方法,BigDecimal的toString中会判断上文中提到的scale属性,如果scale属性是0(小数位的长度为0)则直接调用Long.toString(val),如果scale属性小于0(小数位的长度为负数,也就是说数字是整数,并且最后一位或者最后几位是0,最后面有几个0,scale就是负几)那么在toString方法中就会以科学记数的法表示
解决办法:
 

使用toPlainString替代toString,toPlainString会使用数字的默认展示方式,而不是科学记数法。
 
注:本人使用的是1.2.83版本的fastjson,这版本的是将double转化成BigDecimal。

但是在最新版的fastjson中,是没有转化,double类型还是double类型。

所以以上代码就会变成这样:

 如果想直接输出普通的数字,可以直接使用new BigDecimal(double val);这个构造方法构造出的BigDecimal对象,直接使用toString就是普通的数字