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

我正在查看旧的考试题目(目前是大学的第一年)。我想知道是否有人可以更彻底地解释为什么以下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 

这是因为二进制 :

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

然而,double不能包含无限精度,因此,正如我们近似1/3到0.3333333所以二进制表示必须接近0.1。

扩展的十进制类比

在十进制中你可能会发现

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

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

演示

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

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

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

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

由于’==’和’!=’运算符按位比较数字,在您的情况下0.1 + 0.1 + 0.1就其表示的位而言不等于0.3。

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

作为一般规则,由于舍入错误,永远不要使用double进行迭代(0.1在基础10中编写时可能看起来很好,但尝试在基数2中编写它 – 这是double用途)。 你应该做的是使用一个普通的int变量来迭代并计算它的double精度。

 for (int i = 0; i < 1000; i++) System.out.println(i/10.0); 

首先,我将解释一些关于双打的事情。 这实际上将在十点进行,以便于理解。

取值三分之一并尝试在十分之一表达。 你得到0.3333333333333 ….假设我们需要把它四舍五入到4个位置。 我们得到0.3333。 现在,让我们再添加1/3。 我们得到0.6666333333333 ….其回合至0.6666。 让我们再增加1/3。 我们得到0.9999, 而不是 1。

基数为二和十分之一的情况也是如此。 因为你要经过0.1 10和0.1 10是一个重复的二进制值(如基数十的0.1666666 ……),当你到达那里时,你将有足够的错误错过100。

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

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

试着看看这是否有效