在硬件级别,浮点数表示为二进制数的分数(以 2 为底)。比如小数部分:
0.125
具有值 1/10 + 2/100 + 5/1000 并且以同样的方式具有二进制分数:
0.001
的值为 0/2 + 0/4 + 1/8。这两个分数的值相同,唯一的区别是第一个是十进制分数,第二个是二进制分数。
不幸的是,大多数十进制分数不能在二进制分数中精确表示。因此,一般情况下,您给出的浮点数仅近似为二进制分数以存储在机器中。
以 10 为底的问题更容易解决。以分数 1/3 为例。您可以将其近似为小数:
0.3
或更好,
0.33
或更好,
0.333
等等。不管你写了多少个小数位,结果永远不会正好是 1/3,但它是一个总是更接近的估计值。
同样,无论您使用多少个以 2 为底的小数位,十进制值 0.1 都不能完全表示为二进制小数。在底数 2 中,1/10 是以下周期数:
0.0001100110011001100110011001100110011001100110011 ...
停在任何有限数量的位上,你会得到一个近似值。
对于 Python,在典型的机器上,浮点数的精度使用 53 位,因此输入十进制 0.1 时存储的值是二进制小数。
0.00011001100110011001100110011001100110011001100110011010
接近但不完全等于 1/10。
由于浮点数在解释器中的显示方式,很容易忘记存储的值是原始小数的近似值。 Python 仅显示以二进制形式存储的值的十进制近似值。如果 Python 要输出存储为 0.1 的二进制近似值的真正十进制值,它将输出:
>>> 0.1
0.1000000000000000055511151231257827021181583404541015625
这比大多数人预期的要多得多,因此 Python 显示一个四舍五入的值以提高可读性:
>>> 0.1
0.1
重要的是要理解,这实际上是一种错觉:存储的值并不完全是 1/10,只是在显示屏上存储的值被四舍五入。只要您使用这些值执行算术运算,这一点就会变得很明显:
>>> 0.1 + 0.2
0.30000000000000004
这种行为是机器浮点表示的本质所固有的:它不是 Python 中的错误,也不是代码中的错误。您可以在使用硬件支持计算浮点数的所有其他语言中观察到相同类型的行为(尽管某些语言默认情况下不会使差异可见,或者并非在所有显示模式下都可见)。
另一个惊喜是这个固有的。例如,如果您尝试将值 2.675 舍入到小数点后两位,您将得到
>>> round (2.675, 2)
2.67
round() 原语的文档表明它舍入到离零最近的值。由于小数部分正好在 2.67 和 2.68 之间,因此您应该期望得到(二进制近似值)2.68。然而,情况并非如此,因为当小数部分 2.675 转换为浮点数时,它是通过一个近似值存储的,其精确值为:
2.67499999999999982236431605997495353221893310546875
由于近似值比 2.68 更接近 2.67,因此舍入向下。
如果您遇到将十进制数字舍入一半很重要的情况,您应该使用十进制模块。顺便说一句,decimal 模块还提供了一种方便的方法来“查看”为任何浮点数存储的确切值。
>>> from decimal import Decimal
>>> Decimal (2.675)
>>> Decimal ('2.67499999999999982236431605997495353221893310546875')
0.1 不完全存储在 1/10 中的另一个后果是 0.1 的十个值的总和也不等于 1.0:
>>> sum = 0.0
>>> for i in range (10):
... sum + = 0.1
...>>> sum
0.9999999999999999
二进制浮点数的算术具有许多这样的惊喜。 “0.1”的问题在下面的“表示错误”部分中详细解释。有关此类意外的更完整列表,请参阅浮点的危险。
确实没有简单的答案,但是不要过分怀疑浮动虚拟数!在 Python 中,浮点数运算中的错误是由底层硬件引起的,并且在大多数机器上,每次操作的错误率不超过 2 ** 53。这对于大多数任务来说是非常必要的,但您应该记住,这些不是十进制运算,并且对浮点数的每个运算都可能会遇到新的错误。
尽管存在病态案例,但对于大多数常见用例,您只需将显示屏上的小数位数四舍五入即可获得预期结果。有关如何精细控制浮点数的显示,请参阅字符串格式语法了解 str.format () 方法的格式规范。
这部分答案详细解释了“0.1”的例子,并展示了如何自己对这类案例进行精确分析。我们假设您熟悉浮点数的二进制表示。术语表示错误意味着大多数十进制分数不能精确地用二进制表示。这就是为什么 Python(或 Perl、C、C++、Java、Fortran 等)通常不以十进制显示确切结果的主要原因:
>>> 0.1 + 0.2
0.30000000000000004
为什么? 1/10 和 2/10 不能用二进制分数精确表示。但是,今天(2010 年 7 月)的所有机器都遵循 IEEE-754 浮点数算术标准。大多数平台使用“IEEE-754 双精度”来表示 Python 浮点数。双精度 IEEE-754 使用 53 位精度,因此在读取计算机时会尝试将 0.1 转换为 J / 2 ** N 形式的最接近的小数,其中 J 是正好 53 位的整数。重写:
1/10 ~ = J / (2 ** N)
在:
J ~ = 2 ** N / 10
记住 J 正好是 53 位(所以> = 2 ** 52 但
>>> 2 ** 52
4503599627370496
>>> 2 ** 53
9007199254740992
>>> 2 ** 56/10
7205759403792793
所以 56 是 N 的唯一可能值,它正好为 J 留下 53 位。因此,J 的最佳可能值是这个商,四舍五入:
>>> q, r = divmod (2 ** 56, 10)
>>> r
6
由于进位大于 10 的一半,通过四舍五入获得最佳近似值:
>>> q + 1
7205759403792794
因此,“IEEE-754 双精度”中 1/10 的最佳近似值是 2 ** 56 以上,即:
7205759403792794/72057594037927936
请注意,由于向上舍入,结果实际上略大于 1/10;如果我们没有四舍五入,商将略小于 1/10。但绝不是正好是 1/10!
所以计算机永远不会“看到”1/10:它看到的是上面给出的精确分数,使用“IEEE-754”中的双精度浮点数的最佳近似值:
>>>. 1 * 2 ** 56
7205759403792794.0
如果我们将这个分数乘以 10 ** 30,我们可以观察到它的小数点后 30 位的强权值。
>>> 7205759403792794 * 10 ** 30 // 2 ** 56
100000000000000005551115123125L
意味着存储在计算机中的确切值大约等于十进制值 0.100000000000000005551115123125。在 Python 2.7 和 Python 3.1 之前的版本中,Python 将这些值四舍五入到小数点后 17 位,显示“0.10000000000000001”。在当前版本的 Python 中,显示的值是分数尽可能短的值,而在转换回二进制时给出完全相同的表示,只是显示“0.1”。