Java 为什么使用double的for循环无法终止

Java 为什么使用double的for循环无法终止,java,for-loop,floating-point-precision,Java,For Loop,Floating Point Precision,我正在翻阅旧的试题(目前是大学一年级),我想知道是否有人能更透彻地解释一下为什么下面的for循环没有在应该结束的时候结束。为什么会发生这种情况?我知道它跳过100.0是因为舍入错误或其他原因,但为什么呢 for(double i = 0.0; i != 100; i = i +0.1){ System.out.println(i); } 数字0.1不能准确地用二进制表示,就像1/3不能准确地用十进制表示一样,因此您不能保证: 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.

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

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

数字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
这是因为:

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


扩展十进制类比 在十进制中,你可能会发现

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

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

演示
安德里亚·利吉奥斯(Andrea Ligios)提供的A显示了这些错误的累积。

首先,我要解释一些关于双打的事情。为了便于理解,这实际上将在基数10中进行

取三分之一的值,试着用十进制表示。你得到0.3333。。。。假设我们需要把它四舍五入。我们得到0.3333。现在,让我们再加上1/3。我们得到0.6666333。。。。四舍五入到0.6666。让我们再加上1/3。我们得到的是0.9999,不是1

同样的事情也发生在基数2和十分之一上。因为你的值是0.110,而0.110是一个重复的二进制值(比如0.1666666…以10为基数),所以当你到达那里时,你会有足够多的错误错过100


1/2可以用十进制表示,1/5也可以。这是因为分母的素因子是基因子的子集。对于以10为基数的三分之一或以2为基数的十分之一,情况并非如此。

作为一般规则,由于舍入错误,切勿使用
double
进行迭代(0.1在以10为基数编写时看起来不错,但尝试以2为基数编写,这正是
double
使用的)。您应该使用一个普通的
int
变量来迭代并从中计算
double

for (int i = 0; i < 1000; i++)
  System.out.println(i/10.0);
for(int i=0;i<1000;i++)
系统输出打印LN(i/10.0);
计算机(至少是当前的计算机)处理二进制数据。此外,计算机在其算术逻辑单元(即32位、64位等)中进行处理有长度限制。 以二进制形式表示整数很简单,相反,我们不能对浮点表示同样的事情

如上所示,根据IEEE-754,有一种表示浮点的特殊方式,处理器生产商和软件厂商也接受这种方式,这就是为什么每个人都必须了解它的原因

如果我们看一下java中double的最大值(double.MAX_value)是1.7976931348623157E308(>10^307)。只有64位才能表示大量数字,但问题是精度。

作为“==”和“!=”运算符按位比较数字,在您的情况下,0.1+0.1+0.1不等于0.3表示的位

作为结论,为了在几位中拟合出巨大的浮点数,聪明的工程师决定牺牲精度。如果您使用的是浮点运算,则不应使用“==”或“!=”除非你确定你在做什么。

它应该是(双a=0.0;a<100.0;a=a+0.01)


试着看看这是否管用。来吧,他是说为什么循环永远不会结束,而不是应该在100时阻塞。一点灵活性并没有害死猫;)很抱歉,我试着说这是另一种方式,但每次stackoverflow说“我不允许使用那个词”:P@Patidati你是说你想说“问题”吗。平心而论,你可以说“是什么导致这个for循环无法退出”“我知道它跳过100.0是因为舍入错误还是什么?但是为什么?”-听起来你根本不理解它……避免这种错误的简单方法:记住
==
=double
/
float
s一起使用时,code>都是错误的。您可以检查结果是否在某个点的某个公差范围内,在这种情况下,执行类似于
abs(result-x)
的操作,也可以简单地使用
等。(在你的情况下,
<100
谢谢你,是的,我知道,但这就是考试中问题的措辞:p我想这就是他们想让我们意识到的:)!对于迭代器变量,使用浮点数并不是一个特别的问题;问题在于使用
!=
作为终止条件!如果循环的结构是
(双i=0.0;i<100;i=i+0.1)
它会很好地终止。我建议更好的一般规则是始终使用浮点数的不等式。许多语言都有一个名为“Epsilon”或类似的内置常量(请查看文档以了解您选择的语言),您可以这样使用:
如果(myDouble-double.Epsilonfor (int i = 0; i < 1000; i++)
  System.out.println(i/10.0);