奇怪的for循环问题
我不确定这是否是一个 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
--输出:
0
.05
.1
--snip--
.95
在循环后可以通过 print 语句验证 w 的值为 1。
我已尽可能地验证任何步长小于或等于0.05都将产生此错误。大于0.05的步长应该是正常的。我验证了19分之1(0.052631579)可以打印出1。(显然,像19.9或10.5之类的小数分母将不会产生 [0,1] 范围内的输出。)这可能不是语言本身的错误吗?解释器和普通的 Lua 文件都会产生这个错误。
原文链接 https://stackoverflow.com/questions/7237773
这是一个四舍五入的问题。问题在于0.05被表示为浮点二进制数,而它在二进制中没有精确的表示形式。在2进制下,它是一个类似于十进制的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
并且它将正确地工作。一般来说,如果可以避免使用浮点数(即带小数点的数)来控制循环,则最好不要使用它。
这是一个浮点数的问题。计算机不能精确地表示浮点数。微小的舍入误差使得20次+0.05的相加结果并不精确等于1.0。 阅读这篇文章:"每个程序员都应该了解的浮点数算法。"
为了达到所需的行为,可以循环i从1到20,然后将f设为i×0.05。
这不是 Lua 的 bug。在下面的 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;
}
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
这是浮点不精确的结果。二进制64位浮点数无法存储0.05,因此结果将被四舍五入为比0.05略大的数。这种舍入误差会在重复求和中保留,最终的值将略大于1.0,因此不会被显示出来。