您的位置:首页 > 理论基础

Java(其实是计算机系统的通病,而不单单是Java的问题,C、C++等任何语言都有这个问题)关于小数的运算结果,不正确不精确,原因剖析,及解决办法

2017-11-03 11:39 1471 查看
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等其他任何数据类型的参与,否则必不精确。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐