为什么使用双精度的 for 循环无法终止

2022-09-01 15:04:28

我正在浏览旧的考试问题(目前是大学一年级),我想知道是否有人可以更彻底地解释为什么下面的循环没有在它应该结束的时候结束。为什么会发生这种情况?我知道它跳过100.0是因为舍入错误或其他原因,但为什么?for

for(double i = 0.0; i != 100; i = i +0.1){
    System.out.println(i);
}

答案 1

数字 0.1 不能用二进制值精确表示,就像 1/3 不能用十进制数精确表示一样,因此你不能保证:

0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1==1

这是因为在二进制文件中:

0.1=(binary)0.00011001100110011001100110011001....... forever

然而,双精度不能包含无限精度,因此,正如我们近似于1/3到0.33333333一样,二进制表示也必须近似于0.1。


扩展的十进制类比

在十进制中,您可能会发现

1/3+1/3+1/3
=0.333+0.333+0.333
=0.999

这是完全相同的问题。它不应该被视为浮点数的弱点,因为我们自己的十进制系统也有同样的困难(但是对于不同的数字,具有基数3系统的人会发现我们很难表示1/3很奇怪)。然而,这是一个需要注意的问题。

演示

Andrea Ligios提供的现场演示显示了这些错误的积累。


答案 2

计算机(至少是当前计算机)使用二进制数据。此外,计算机在其算术逻辑单元(即32位,64位等)中处理的长度限制。以二进制形式表示整数很简单,相反,我们不能对浮点说同样的事情。64 bits floating point representation

如上所示,根据IEEE-754,有一种特殊的方式来表示浮点,处理器生产者和软件人员也认为这是事实上的,这就是为什么每个人都知道它很重要。

如果我们看一下java中双精度值的最大值(Double.MAX_VALUE)是1.7976931348623157E308(>10^ 307)。只有64位,可以表示巨大的数字,但问题是精度。

由于'=='和'!='运算符按位比较数字,因此在您的情况下,0.1 + 0.1 + 0.1在表示位方面不等于0.3。

总而言之,为了在几个位中拟合巨大的浮点数,聪明的工程师决定牺牲精度。如果您正在处理浮点数,则不应使用“==”或“!=”,除非您确定自己在做什么。