Rüp*_*ure 4 c floating-point loops loop-counter floating-point-precision
我们可以安全地使用浮点数作为循环计数器,并在每次迭代时按小数量递增/递减它们,就像下面看似无风险的程序一样吗?当然我知道使用浮点数作为==运算符的操作数是愚蠢的要做的事情.但是为了"正常"目的使用浮动作为其他比较操作的操作数有什么问题?通过"正常",我的意思是,即使浮点数可能不是数字的精确数字表示,但是不是0.000000001无关的变化,在大多数情况下可以忽略吗?(例如,在以下程序中甚至不明显)
但是说,这就是我的理解.假设表示不精确,5.0实际上是4.999999.因此,当我们在每次迭代时继续递减0.5时,与0的最后一次比较可能会变为假,并且循环可能会因为相差0.000001,不显示当前输出的最后一行.我希望你能得到我的漂移.我怎么了?
#include<stdio.h>
int main(void)
{
float f;
for(f=5.0;f>=0;f-=0.5)
printf("%f\n",f);
}
Run Code Online (Sandbox Code Playgroud)
输出:
5.000000
4.500000
4.000000
3.500000
3.000000
2.500000
2.000000
1.500000
1.000000
0.500000
0.000000
Run Code Online (Sandbox Code Playgroud)
rai*_*7ow 11
不,这是不安全的,原因在于你的问题.考虑一下:
#include<stdio.h>
int main(void) {
float f = 1.0;
for(;f>0;f-=0.1)
printf("%f\n",f);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这个例子在初始化时似乎工作得很好.但是将其更改为3.0 - 事情很快就开始变得更有趣了:f1.0
2.600000
2.500000
2.400001
...
0.000001
Run Code Online (Sandbox Code Playgroud)
......导致臭名昭着的"一个接一个"的失败.
你认为你可能安全>=而不是>?再想一想:
float f = 5.0;
for(;f>=1;f-=0.4)
printf("%f\n",f);
...
3.400000
3.000000
2.599999
2.199999
1.799999
1.399999
Run Code Online (Sandbox Code Playgroud)
......然后我们一个接一个地去(0.99999小于1).
只要起始值,减量和所有减量的结果都可以在浮点类型提供的精度内无误差地表示,那么使用它是安全的.注意,"无错误"在这里表示0绝对错误,非常小的错误仍然被认为是错误.
在你的情况下,初始值5.0和递减量0.5能没有错误的表示,并且4.5,4.0,3.5,...,0.0也可以用23位精度范围内无误差来表示float.在您的情况下是安全的.
如果让我们说起始值是4000000.0,并且减量是0.00390625(2-8),那么你就麻烦了,因为减量的结果不能在23位精度float类型中没有错误地表示,尽管起始值和减量金额可以正确表示.
但是,在这种情况下,当积分类型更可靠时,我认为使用浮点没有意义.您不必浪费脑细胞检查上述条件是否适用.
由于浮点表示的问题,只要可能,首选整数值超过浮点.
而不是使用浮点数作为循环控件,重写您的逻辑以使用整数:
需要递减你的柜台.5吗?将起始值加倍并减1:
float f = 5.0;
int i = f * 2;
for(; i >= 0; i--)
printf("%f\n", i / 2.0);
Run Code Online (Sandbox Code Playgroud)
需要递减.1吗?
float f = 5.0;
int i = f * 10;
for(; i >= 0; i--)
printf("%f\n", i / 10.0);
Run Code Online (Sandbox Code Playgroud)
对于问题中的示例,这是一种简单的方法.当然不是唯一的方法或最正确的方法.更复杂的示例可能需要将逻辑重新设计稍微不同.无论情况如何.
我的观点是,我认为暂缓使用实际浮点值直到最后一刻可以减少由于表示引起的错误.