浮点型的有效位数?精度丢失?

本文最后更新于:2021年10月27日 晚上

今天发现了一个奇怪的现象:当我在 Python 使用 1.0000000000000001e-16(即 0.0000000000000001)去加另一个数的时候,发现无论怎么加,另一个数就是不变。这个特别长的浮点数就像 0 一样,系统自动把最后面那位 1 给省略了。

其实,早在很久以前我就已经发现了这个问题:0.1 + 0.2 竟然等于 0.30000000000000004,并且 0.1 + 0.2 != 0.3,这很颠覆我的三观。

有这样一段代码可以很好地显示出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> f: float = 0.0
>>> for i in range(10):
... f += 0.1
... print(f)
...

0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
>>>

我在网上翻阅了一些资料,发现原来不止 Python,还有其他任何使用二进制浮点数的编程语言都会有这个问题。

原来,现在的计算机几乎都采用了 IEEE-745 标准。一些浮点数在转化为二进制的时候会出现无限循环:

1
2
0.1 => 0.0001 1001 1001 1001 1001...
0.2 => 0.0011 0011 0011 0011 0011...

所以 0.1 + 0.2 在计算并转换为二进制的时候也不可避免地出现无限循环:

1
0.3 => 0.0100 1100 1100 1100 1100...

可以看出,0.10.2 在转换为二进制时就发生了一次精度丢失,而对于计算后的二进制又有一次精度丢失,所以最终的结果也就不准确。

Python 中,似乎可以这样来计算:

1
2
3
4
5
6
7
8
9
>>> from decimal import Decimal
>>> Decimal(0.1) + Decimal(0.2)
Decimal('0.3000000000000000166533453694')
>>> float(Decimal(0.1) + Decimal(0.2))
0.30000000000000004
>>> Decimal('0.1') + Decimal('0.2')
Decimal('0.3')
>>> float(Decimal('0.1') + Decimal('0.2'))
0.3

先扯这么多吧!今天收获了一个硬核的芝士~