Java(其实是计算机系统的通病,而不单单是Java的问题,C、C++等任何语言都有这个问题)关于小数的运算结果,不正确不精确,原因剖析,及解决办法
Java(其实是计算机系统的通病,而不单单是Java的问题)关于小数的运算结果,不正确不精确,原因剖析,及解决办法
这么神奇?
试一把。还真是:
从输出结果上看,出在运算上,而不是出在存储上。如果问题出在存储上,0.07这个应该也不能正常显示。那么怎么解决呢?
那既然是计算导致的问题,存储是没有问题,为什么这篇文章却说,2.4这个数字,计算机没办法用二进制精确表示呢??
有些人跟我有相同的疑问:
这么看来,计算机的确不能精确的表示小数:
当我们 double a = 0.1的时候, 0.1(十进制) = 0.0001100110011001(二进制),实际是表示为浮点数的,这里就略过了。计算机里也只能保存到0.0001100110011001(二进制)的浮点形式。 而 0.0001100110011001(二进制) = 0.0999908447265625(十进制)。也就是说内存中存放的数只能表示到0.0999908447265625。 |
那为什么double a=0.1这个变量,输出出来的时候,显示的不是0.0999908447265625的,而是精确的0.1?
答案是:这是Java给你制造的假象。。
怎么处理,就可以精确了呢?答案是:BigDecimal。
步骤如下:
1)将double类型转换成string类型
2)将string类型转换成BigDecimal对象
3)使用BigDecimal对象的方法如add等进行加减乘除运算。
疑问:既然0.06存入计算机中就不是精确的0.06如0.0599999,那么转成string不就是也不是精确的0.05999999了吗,为什么转出来却没有问题?
如下我做了测试,原因还是假象,是因为Double.toString并不是给你显示的精确的0.599999,而是给你一个比较规整的0.06,就是这么坑爹,处处是假象。
但是是不是用了bigDecimal就一定没问题了呢?
不是的,比如,如下例子:
我现实中,要将下图中的两个很长的小数相加,但是只有方法2做到了精确的相加,其他的Java都背地里做了手脚给外界现实一个假象。
方法1和最后的方法问题出在哪儿?
可以下debug就很清楚了,在定义Double类型的变量时,就已经是假象了。
并且BigDecimal.valueOf()只能处理double和long型的数据。
总结:只要记住如下3点就行。
1无法精确存储小数。计算机无法100%精确地存储小数,无法,never,肯定会有误差。
2假象。如果你存0.0.6到计算机输出的的结果还是0.06,那么,你看到的就是Java(其实是Java的jdk封装的类,如system.out.println、Double.toString等)给你展现的假象,他偷偷的将存储在计算机里的真实结果给你偷偷地处理了。
3只要BigDecimal和String,绝不要Double等任何其他数据类型的参与。如果想要精确计算,只需要BigDecimal和String两个类就行了,不要Double等其他任何数据类型的参与,否则必不精确。