使用浮动变量循环计数器和非他们的分数递增/递减“==”条件的危险吗?(Any risk of usi

2019-09-03 20:05发布

我们是否可以安全使用浮点数作为循环计数器和递增/在每个迭代的分数数量递减他们,就像在下面的看似无风险的项目?我当然知道,使用浮标作为操作数的==操作符是一个愚蠢的事情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

Answer 1:

不,这不是安全的,在你非常的问题给出的理由。 试想一下:

#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)。



Answer 2:

只要初始值,则减少量和所有的递减的结果,可以与由浮点类型提供的精度范围内无误差来表示,则可以安全使用。 需要注意的是“没有错误”在这里是指0的绝对误差,误差很小仍然是考虑的一个错误。

在你的情况下,初始值5.0和减少量0.5可以无误差来表示,并且4.54.03.5 ,..., 0.0还可以与23比特的精度范围内无误差表示float 。 这是在您的情况是安全的。

如果假设起始值为4000000.0和减少量为0.00390625 (2-8),那么你就有麻烦了,因为递减的结果,离不开中的23位精度误差来表示float型,虽然初始值和减少量可以正确地表示。

但是,我看到在使用浮点,当整体式就是在这样的情况下,更可靠的没有意义的。 你不必浪费脑细胞在检查我上述的条件是否适用。



Answer 3:

在浮点不想整数值尽可能的简单,因为与浮点表示问题。

而不是使用浮点数作为循环控制,返工你的逻辑使用整数:

需要通过递减贵方.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);

这是在问题中所说的简单方法。 当然不是唯一的方法或最正确的。 更复杂的例子,可能需要返工逻辑有点不同。 任何适合的情况。

我的观点,我想是保持了与实际浮点值工作,直到最后一刻,以减少介绍,由于表示错误。



Answer 4:

工程师和科学家经常写迭代程序,其中一个浮点值步骤,通过以小的增量值的范围。

例如,假设“时间”变量需要从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.04.0中的步骤0.5 ,使用三个技术途径:

常数 - 以输入端的平均在端点处,评估F(平均输入),并假定该函数是在所述范围内是恒定。

线性 - 评估函数在端点处,然后使用端点函数值的线性内插在两者之间。

非线性 - 线性在范围内插功能的输入,并在每个评价点,评估的内插的输入的功能。



文章来源: Any risk of using float variables as loop counters and their fractional increment/decrement for non “==” conditions?