解决一个价格转换显示的bug


double a=Double.parseDouble(3.80);


long price=new Double(a*100).longValue();


结果是3.799999999.......


大部分程序员都知道浮点类型不能用来做精确表示和运算,对根本原因能随口说来的可能并不多,借着这次机会,把涉及到计算机原理的知识点剖析下。


出现上述结果的根本原因是计算机使用二进制01无法准确表示某些带小数位的十进制数据


十进制数据转换成二进制数据,需经过如下计算:


1、整数部分


用该整数连续除以2,取余数,商再除以2,直到商等于0为止,最后把得到的余数按相反的顺序排列,即“除2取余法”


2、小数部分


小数部分“乘2取整,顺序排列”法,小数部分乘以2,从乘积中取整数部分,剩余的小数部分乘以2,


再把乘积的整部部分取出,剩余的小数部分乘以2,直到乘积中的小数部分为0或达到所要求的精度为止,把取出的整数部分顺序排列,即“乘2取整法”。


3、相加


最后把整数部分和小数部分相加,得到二进制数据


例如:十进制的3.8转换成二进制数值,步骤如下:


3(整数部分)


3/2=1................1


1/2=0.................1


十进制3 转换为二进制 11


0.8(小数部分)


0.8*2=1.6................1


0.6*2=1.2.................1


0.2*2=0.4.................0


0.4*2=0.8.................0


0.8*2=1.6................1


....


十进制0.8 转换为二进制 (0.11001100.....) 后边无限循环1100这段二进制数值,因此也就出现了上述的3.799999的情况。



java 的浮点类型都依据 IEEE 754 标准。IEEE 754 定义了32 位和 64 位双精度两种浮点二进制小数标准。


IEEE 754 用科学记数法以底数为 2 的小数来表示浮点数。32 位浮点数用 1 位表示数字的符号,用 8 位来表示指数,用 23位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二进制(底数 2 )小数来表示。对于64位双精度浮点数,用 1 位表示数字的符号,用 11 位表示指数,52 位表示尾数。


在 IEEE 754 的 double 没有办法表示出 0.8,只能得到一个近似值。



float和double只能用来做科学计算或者是工程计算,在商业计算等精确计算中,用java.math.BigDecimal。


BigDecimal


BigDecimal的解决方案就是,不使用二进制,而是使用十进制(BigInteger)+小数点位置(scale)来表示小数。


上述的39.8=398  * 0.1^1 这种表示方式下,避免了小数的出现,就不会有精度问题了。