我不确定这是不是一个bug,所以我想也许你们大家可能想看看。
问题出在这个代码上:
for i=0,1,.05 do
print(i)
end
输出应为:
0
.05
.1
--snip--
.95
1
相反,输出是:
0
.05
.1
--snip--
.95
同样的问题发生在while循环中:
w = 0
while w <= 1 do
print(w)
w = w + .05
end
--output:
0
.05
.1
--snip--
.95
w的值为1,可以在循环后通过print语句验证。
我已尽可能验证任何小于或等于.05的步骤都会产生此错误。任何高于.05的步骤应该没问题。我确认1/19(0.052631579)确实打印了1.(显然,像19.9或10.5这样的小数分母不会产生[0,1]包含的输出。)是否有可能这不是语言的错误?解释器和常规Lua文件都会产生此错误。
答案 0 :(得分:8)
这是一个四舍五入的问题。问题是0.05表示为浮点二进制数,并且它没有二进制的精确表示。在基数2(二进制)中,它是一个重复的十进制数,类似于基数10中的1/3数。当重复添加时,舍入产生的数字略大于1.它只是非常非常略大于1 ,所以如果你打印出来,它会显示1作为输出,但它不完全是1。
> x=0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05+0.05
> print(x)
1
> print(1==x)
false
> print(x-1)
2.2204460492503e-16
所以,正如你所看到的,虽然真的接近1,但实际上稍微多了一点。
当我们有重复分数时,类似的情况可以以十进制形式出现。如果我们将1/3 + 1/3 + 1/3加在一起,但我们必须舍入到六位数才能使用,我们将添加0.333333 + 0.333333 + 0.333333并获得0.999999,这实际上不是1.这是一个二进制数学的类似情况。 1/20不能用二进制精确表示。
请注意,乘法的舍入略有不同,所以
> print(0.05*20-1)
0
> print(0.05*20==1)
true
因此,您可以重写代码
for i=0,20,1 do
print(i*0.05)
end
它会正常工作。一般情况下,建议不要使用浮点数(即带小数点的数字)来控制循环,这是可以避免的。
答案 1 :(得分:3)
这是浮点不准确的结果。 binary64浮点数无法存储0.05,因此结果将四舍五入到略大于0.05的数字。这个舍入误差保留在重复的总和中,最终最终值略大于1.0,因此不会显示。
答案 2 :(得分:2)
这是一个浮点事物。计算机不完全代表浮点数。微小的舍入误差使得20 + +0.05的增加不会导致精确的1.0。 看看这篇文章:“What every programmer should know about floating-point arithmetic.”
要获得所需的行为,您可以将i循环超过1..20,并设置f = i * 0.05
答案 3 :(得分:1)
这是不是 Lua中的错误。同样的事情发生在下面的C程序中。正如其他人所解释的那样,这是由于浮点不准确,更确切地说,是由于0.05不是二进制分数(即没有有限的二进制表示)。
#include <stdio.h>
int main(void)
{
double i;
for (i=0; i<=1; i+=0.05) printf("%g\n",i);
return 0;
}