无限循环是由于计算机表示浮点数的方式不准确。
计算机只能为每个变量分配一定数量的位。您可以将其视为在小数点后存储有限数量的数字。在以十为底的情况下,您可以将 1/10 精确地表示为 0.1,因此您可以d
在 0.0、0.1、0.2、...、10.0 的过程中以完美的精度书写。但想象一下以 1/7 递增。这是一个重复的小数 (0.142857142857...),因此无法以有限的位数以 10 为基数精确表示。如果计算机只能为每个变量存储 2 位小数,则d
看起来像这样:
分数 实际值 储值
1/7 0.142857... 0.14
2/7 0.285714... 0.28
3/7 0.428571... 0.42
4/7 0.571428... 0.56 ←不匹配
5/7 0.714285... 0.70
6/7 0.857142... 0.84
7/7 1.0 0.99
请注意从 4/7 开始的每个值的舍入误差。(实际上,由于截断,
每个值都有一个舍入误差;当数字不匹配时会更加明显。)要注意的重要一点是,我们存储多少位并不重要;除非它是无限的,否则
总会有舍入误差。
因此,在基数为 10 的情况下,将变量增加 0.1 既简单又“干净”,因为该数字可以用有限的位数精确表示。但对于由重复小数表示的数字,例如 1/6、1/7、1/13 等,情况并非如此。
计算机以二进制(以 2 为底)存储数字,但概念完全相同。分数 1/10 没有以 2 为底的精确表示。计算机必须通过将 2 的不同幂加在一起来表示每个数字:8、4、2、1、½ ¼、⅛ 等数字。例如:
15 = 8 + 4 + 2 + 1 = 1111 b
10 = 8 + 0 + 2 + 0 = 1010 b
2½ = 0 + 0 + 2 + 0 + ½ = 0010.1 b
¾ = 0 + 0 + 0 + 0 + ½ + ¼ = 0010.11 b
但我们不能仅使用 2 的幂来准确表示 1/10:
1/10 = 0/2 + 0/4 + 0/8 + 1/16 + 1/32 + 0/64 + 0/128 + 1/256 +
1/512 + 0/1024 + 0/2048 + 1/ 4096 + 1/8192 + ...
1/10 = 0.0001100110011... b
同样,假设我们的小数位数有限。你会看到,无论我们使用多少,如果我们继续添加 1/10,最终都会产生舍入误差。这正是您的程序中发生的情况:重复添加 1/10 的二进制表示将在总和达到 10.0 之前产生舍入误差,因此条件d != 10.0
将始终为真。
正因为如此,在处理浮点数时,最好的做法是像其他几个人建议的那样:永远不要测试浮点变量是否相等;总是使用不等式。您可以使用 消除无限循环while (d < 10.0)
。