《零基础学 Java》 FAQ 之 3- 为什么计算机里的浮点数不精确
用计算机的有限 PK 小数的无限
首先一个事实是,计算机是用固定的字节数来表示一个浮点数的。我们就拿double来举例,一个 double 变量占用8个字节,和整数的 long 是一样的。
但是小数的可能性有多少呢?从0.1到0.2,就有无数个小数。所以,浮点数不可能完全精确的表示小数。
在这里我大概说一下计算机是如何让处理小数的。怎么用有限的存储,表示无限的小数呢?原理很简单,就是舍弃精度。
比如说(没有写程序验证,只是从道理上说)对于从6.0000000000000001到6.0000000000000008的小数,计算机是用同一个二进制的数字来表示的,同时,计算和显示也都是用6.0000000000000001。这样就相当于舍弃了精度,让浮点数可以“近似的”表示很大范围内的浮点数。
当然,如果我们要表示整数部分很大的数字,比如123456789987654321.000009,那么精度将会变得更低。
所以大家理解为什么叫做浮点数了吗?因为浮点数的这个点,是指小数点;浮,是指这个小数点会浮动。如果整数部分过大,那么小数点后面的位数和精度就会变小。舍弃小数精度,让值更接近像表示的值。
其实浮点数在计算机里,是一个大学问。最开始PC上的CPU,是不能从硬件层面支持浮点数计算的,都要靠软件模拟,速度非常慢。当时牛X的PC,会带一个浮点数的协处理器,专门用来从硬件层面支持浮点数的运算。如果想深入了解为什么浮点数这么复杂,请参考下文:
What Every Computer Scientist Should Know About Floating-Point Arithmetic
浮点数不精确的例子
浮点数的精度涉及到浮点数本身的表示形式,理解起来还是略复杂的。简化版大概可以这么理解,在浮点数的世界里,一个具体的二进制的数字,其实表示的是一个范围,比如说下面的三行代码:
它们的输出是一样的
```
0x1.54484932d2e74p-120
0x1.54484932d2e74p-120
0x1.54484932d2e74p-120
````
这个意思是这三个数字,转换成浮点数的二进制后其实是一样的。这就是浮点数的精度的直观感受——非常相近的数字,二进制的表示形式是一样的。
就好像电子的轨道一样,不是说电子可以在任意轨道绕着原子核旋转,或者跃迁,光电效应了解一下?电子的轨道只能在固定的满足某个条件的轨道转圈。计算机也一样,不能表示无限精度的数字,只能尽力……
``System.out.println(0.00000000000000000000000000000000000100000000000000032 == 0.00000000000000000000000000000000000100000000000000022);
``
比较也是一样的,正因为浮点数的这种不精确,导致其进行精确的比较是不可靠的,比如上面这两个不一样的数字,其实转换成二进制其实是一样的。输出的结果是true
同样的道理,这种不精确可能会积累,放大,所以浮点数的比较运算,比较推荐的是,求两个数字的差,然后让这个差取绝对值,和一个小到对业务没有影响的值比较,如果比这个值还小,就认为两个浮点数是相等的。
这篇文章来自极客时间推出的《零基础学Java》中的FAQ。除了在每节视频课下方回答大家的问题之外,针对大家提出的优质问题或者普遍问题,如果需要更大篇幅的文章解答,则会在FAQ中以文章的方式给出回答。带你零基础入门,夯实Java,课程地址:https://time.geekbang.org/course/intro/181
版权声明: 本文为 InfoQ 作者【臧萌】的原创文章。
原文链接:【http://xie.infoq.cn/article/45d2a4816f120f625c589bb4d】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论