如何理解double精度丢失问题?
关注者
49被浏览
99,8066 个回答
很简单,从二进制角度考虑问题即可:把「0.1」转成二进制的表示,然后还原成十进制,就能看出问题。
一、把 0.1 转成二进制表示
我们知道 DEC(1) 就是 BIN(1),但是 DEC(0.1) 怎么转换成二进制?
对了!用除法:
0.1 = 1 ÷ 10
很简单,二进制就是要算
1 ÷ 1010
我们回到小学的课堂,来列竖式吧:
0.000110011...
------------------
1010 ) 1 0000
1010
------
1100
1010
----
10000
1010
-----
1100
1010
----
10
相信上过小学的你一定会发现,除不尽,除出了一个无限循环小数:二进制的 0.0001100110011...
二、舍入
我们得把 0.0001100110011... 放进一个 double「双精度浮点数」里面
双精度浮点数能表示多少精度呢?查看文档会发现:
- 半精度(16bit):11 位有效数字
- 单精度(32bit):24 位有效数字
- 双精度(64bit):53 位有效数字
- 四精度(128bit):113 位有效数字
好吧,双精度是 53 位有效数字
0.00011001100110011001100110011001100110011001100110011001 10011...
方便起见,我在第 53 个有效数字后面加了个空格。
那么问题来了:十进制数我们可以四舍五入,二进制怎么办?精神是一样的:待处理部分有没有达到前一位的一半,达到就进位,没达到就舍去。(暂且当作 0 舍 1 入。)
那么我们的 0.1 在 double 中就是
0.00011001100110011001100110011001100110011001100110011001 10011...
0.00011001100110011001100110011001100110011001100110011010
而 1.1 就是
1.0001100110011001100110011001100110011001100110011001 10011...
1.0001100110011001100110011001100110011001100110011010
三、加法
这个很简单,1.1 + 0.1 就是
1.0001100110011001100110011001100110011001100110011010
+ 0.00011001100110011001100110011001100110011001100110011010
------------------------------------------------------------
1.00110011001100110011001100110011001100110011001100111010
因为结果仍然是 double,需要再做一次保留 53 位有效数字和舍入:
1.0011001100110011001100110011001100110011001100110011 1010
1.0011001100110011001100110011001100110011001100110100
四、结果
好了,终于可以回到十进制的世界了,我们把最终结果转换回来:
1.0011001100110011001100110011001100110011001100110100
得到十进制的:
1.20000000000000018
一般的输出函数,在输出浮点数时,都会限制显示的有效数字,即会再做一次四舍五入。题目中的 1.2000000000000002 是这个结果在显示时四舍五入后的结果。
1.20000000000000018
1.2000000000000002
---
正经答题:1.2000000000000002 的原理上面已经一步步分析了。至于各个语言之间的差异,答案是可能会有,比如可能因为选择的舍入规则的不同可能导致的结果的不同;甚至有可能某个语言里的浮点数压根不是 IEEE 754 的浮点数,而是以字符串方式保存的,所以可能没有误差。
原理是一样的,都遵循同样的标准。
根本原因在于,数学意义上的小数不是每个都能用二进制在有限位数内精确的表示。
像 0.1,1.1 这样的小数没有精确的二进制表示,然后求和就不是1.2了。