具有模数的while循环中的浮点比较

时间:2011-08-28 07:31:15

标签: c++ loops floating-point modulus

我想建议如何优化以下while循环:

double minor_interval   = 0.1;
double major_interval   = 1.0;

double start            = 0.0;
double finish           = 10.0;

printf("Start\r\n");

while (start < finish)
{
    printf("Minor interval: %.20f\r\n", start);

    double m = fmod(start, major_interval);
    printf("m: %.20f\r\n", m);

    if (m == 0)
        printf("At major interval: %.20f\r\n", start);

    start += minor_interval;
}

printf("Finished\r\n");

基本上,我正在通过次要间隔递增循环中的计数器,并且如果我处于主要间隔,我想知道每次循环。想象这是用毫米间隔画一个尺子,每当我到达一个主要的间隔时,我想画一厘米。鉴于浮点运算的不准确性,如何修改上述循环以实现我需要的功能?我尝试过使用公差比较模数结果的不同方法,但没有运气。请注意,次要和主要间隔可以是任何值,即minor = 0.4和major = 1.6(以绘制四分之一英里的增量)。

提前致谢。

3 个答案:

答案 0 :(得分:6)

我会在int变量i上使用for循环实现此功能。由于浮点运算的限制,使用整数避免了舍入问题。 [见What Every Computer Scientist Should Know About Floating-Point Arithmetic。]

i0运行到iFinish-1并将time设置为start + i*minor_interval。可以通过将iFinish四舍五入到最近的int来找到(finish-start)/minor_interval的值。

可以以类似的方式处理主轴更新。将major_interval/minor_interval舍入到最近的int,说k。然后每隔k次迭代更新主轴标记。

就代码而言,它看起来像这样:

double round(double r) {
    return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}
...
int iFinish = round((finish-start)/minor_interval);
int k = round(major_interval/minor_interval);
for (int i=0; i<iFinish; i++)
{
    double time = start + i*minor_interval;
    bool isMajor = (i%k == 0);
    ...
}

答案 1 :(得分:0)

您的容忍方法不起作用的原因是它只考虑存储值略高于应有的时间。 (例如当它是1.0001而不是1.如果它是0.999而不是1,那么你的方法将不起作用)

要解决此问题,请检查它是否在预期值的 一侧的容差范围内。

所以,而不是:

if (m == 0)

使用:

if (m < small_value || major_interval - m < small_value)

其中small_value类似于1e-8。

答案 2 :(得分:0)

我会使用整数使循环成为for循环。此外,如果数字,你应该避免==运算符浮点数/双精度数,因为这些tyes具有不确定性。

因此,为了循环的目的,将双精度转换为整数:

numIntervals = (int)((finish - start) / minor_intervals);
majIntervalsGap = numIntervals  / (int)((finish - start) / major_intervals);
double time = start;
for (int loop = 0;; loop < numIntervals; ++loop)
{
    bool isMajor = ((loop % majIntervalsGap) == 0);

    ... do you ourput here as you have the desired info for it
    time += minor_interval;
}