我们是否可以安全使用浮点数作为循环计数器和递增/在每个迭代的分数数量递减他们,就像在下面的看似无风险的项目?我当然知道,使用浮标作为操作数的==操作符是一个愚蠢的事情do.But有什么错用浮标作为操作数为“正常”的目的,其他的比较操作? 通过“正常的”我的意思是,好吧,即使浮动可能不是人数的确切数字表示,而不是像一个变化0.000000001
无关紧要,可在大多数情况下被忽略? (例如,在下面的程序,它是不连表观的)
话虽如此,这是我的apprehension.Suppose表示不准确和5.0其实是4.999999。所以,我们继续在每次迭代0.5递减,0最后比较可能变成假,循环可能退出因将不被显示的0.000001的差,并且电流输出的最后一行。 我希望你得到我的错drift.How我是谁?
#include<stdio.h>
int main(void)
{
float f;
for(f=5.0;f>=0;f-=0.5)
printf("%f\n",f);
}
输出:
5.000000
4.500000
4.000000
3.500000
3.000000
2.500000
2.000000
1.500000
1.000000
0.500000
0.000000
不,这不是安全的,在你非常的问题给出的理由。 试想一下:
#include<stdio.h>
int main(void) {
float f = 1.0;
for(;f>0;f-=0.1)
printf("%f\n",f);
return 0;
}
这个例子似乎工作相当确定时f
被初始化1.0
。 但这种变化至3.0 -事情开始获得方式更有趣的很快:
2.600000
2.500000
2.400001
...
0.000001
......导致臭名昭著的“断接一个的失败。
你认为你可能与安全>=
替代>
? 想再次 :
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
......和关闭接一个,我们又来了(如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);
需要通过递减.1
?
float f = 5.0;
int i = f * 10;
for(; i >= 0; i--)
printf("%f\n", i / 10.0);
这是在问题中所说的简单方法。 当然不是唯一的方法或最正确的。 更复杂的例子,可能需要返工逻辑有点不同。 任何适合的情况。
我的观点,我想是保持了与实际浮点值工作,直到最后一刻,以减少介绍,由于表示错误。
工程师和科学家经常写迭代程序,其中一个浮点值步骤,通过以小的增量值的范围。
例如,假设“时间”变量需要从TMIN的低变到高的tMax
中的步骤deltaT
,其中所有这些变量都是双打。
最明显但不正确的方法如下:
`for( time = tMin; time <= tMax; time += deltaT ) {
// Use the time variable in the loop
}
`
那么,为什么会这样错了吗?
如果DeltaT是小的和/或所述范围是大(或两者),循环可以执行用于数千迭代。
这意味着,通过循环结束, time
已经被数以千计的加法运算的总和计算。
数字似乎“精确”给我们以十进制形式,如0.01时,计算机将它们存储在二进制,这意味着用于该值不准确deltaT
的确是一个近似准确值。
因此在每次添加步骤引入舍入误差非常小的量,并添加了数千这些错误的时间,总误差可以显著。
正确的做法是如下,如果你知道的最小值和最大值,并在每次迭代所希望的变化:
`int nTimes = ( tMax - tMin ) / deltaT + 1;
for( int i = 0; i < nTimes; i++ ) {
time = tMin + i * deltaT;
}
// NOW use a more accurate time variable
// Or alternatively if you know the minimum, maximum, and number of desired iterations:
double deltaT = ( tMax - tMin ) / ( nTimes - 1 );
for( int i = 0; i < nTimes; i++ ) {
time = tMin + i * deltaT;
// NOW use a more accurate time variable
}
`
一般有可用于指定通过一系列步进四个值 - 该范围的低端,该范围的高端,步数取,和增量采取的每一步 - 如果你认识他们中的任意三个,那么你就可以计算出第四个。
正确的循环应当使用的整数计数器,以完成循环的给定次数,并使用范围和增量的低端所示在循环的每一次迭代的开始,计算浮点循环变量。 那么,为什么是更好?
那循环执行的次数,现在是一个整数,它没有在任何增量舍入误差控制,所以没有执行,由于积累的舍入一个太多或一个太少迭代的机会。
时间变量,现在是从一个单一的乘法和单一此外,这还可以引入一些舍入误差计算,但远低于十万增补。 在这情况下+1从何而来?
的1是必要的,以包括该范围的两个端点。 假设tMax
是20和TMIN是10,和deltaT
为2。
所需的时间将是10,12,14,16,18,20,这是一个总的6个时间值,而不是5(五个间隔,如果你想看看这种方式。) ( 20 - 10 ) / 2
产量5,所以你必须添加额外的1获得的6次正确的号码。
看这个另一种方式是,如果N次是数据点的范围内的数目,则nTimes - 1
是数据点之间的间隙的数量。
例如:interpolate.c是在一个循环中,这是在类10分钟刮起插值浮点数的快速和肮脏的例子。 它不是好的代码的例子,但它是一个小巧的程序如何被用来测试出来,一起玩,或者在这种情况下,表现出新的或不熟悉的概念的例子。
本实施例中内插函数f( x ) = x^3
在范围从-1.0
到4.0
中的步骤0.5
,使用三个技术途径:
常数 - 以输入端的平均在端点处,评估F(平均输入),并假定该函数是在所述范围内是恒定。
线性 - 评估函数在端点处,然后使用端点函数值的线性内插在两者之间。
非线性 - 线性在范围内插功能的输入,并在每个评价点,评估的内插的输入的功能。
文章来源: Any risk of using float variables as loop counters and their fractional increment/decrement for non “==” conditions?