floor()表现得很奇怪

时间:2013-03-13 09:16:43

标签: c++ integer rounding floor

我需要一个函数将正双精简到其最接近的整数。潜伏着我,发现这种非常优雅的方式

int x = floor(y + 0.5);

我写了一个简单的测试程序:

double a = 10.0;

for (int i = 0; i < 10; i++) {
  cout << a << "\t" << a + 0.5 << "\t" << floor(a + 0.5) << endl;
  a += 0.1;
}

但我收到一些奇怪的输出

10      10.5    10
10.1    10.6    10
10.2    10.7    10
10.3    10.8    10
10.4    10.9    10
10.5    11      10 <--- should be 11!
10.6    11.1    11
10.7    11.2    11
10.8    11.3    11
10.9    11.4    11
那是什么意思?

感谢 问候 卢卡

4 个答案:

答案 0 :(得分:3)

通过添加0.1,您确实添加了略低于0.1的值。

所以添加0.1 5次与添加0.5次不一样;你没有完全达到这个价值。再次添加.5,你不会超过11,这会产生你观察到的行为。


C程序,例如

#include <stdio.h>
#include <math.h>

int main()
{
    double a = 10.0;
    int i;
    for (i = 0; i < 11; i++) {
        printf("%4.19f\t%4.19f\t%4.19f\n", a, a+.5, floor(a + 0.5));
        a += 0.1;
    }
    printf("\n");
    for (i = 0; i < 11; i++) {
        a = 10.0 + i/10.0;
        printf("%4.19f\t%4.19f\t%4.19f\n", a, a+.5, floor(a + 0.5));
    }
}

显示在其输出

10.0000000000000000000  10.5000000000000000000  10.0000000000000000000
10.0999999999999996447  10.5999999999999996447  10.0000000000000000000
10.1999999999999992895  10.6999999999999992895  10.0000000000000000000
10.2999999999999989342  10.7999999999999989342  10.0000000000000000000
10.3999999999999985789  10.8999999999999985789  10.0000000000000000000
10.4999999999999982236  10.9999999999999982236  10.0000000000000000000
10.5999999999999978684  11.0999999999999978684  11.0000000000000000000
10.6999999999999975131  11.1999999999999975131  11.0000000000000000000
10.7999999999999971578  11.2999999999999971578  11.0000000000000000000
10.8999999999999968026  11.3999999999999968026  11.0000000000000000000
10.9999999999999964473  11.4999999999999964473  11.0000000000000000000

10.0000000000000000000  10.5000000000000000000  10.0000000000000000000
10.0999999999999996447  10.5999999999999996447  10.0000000000000000000
10.1999999999999992895  10.6999999999999992895  10.0000000000000000000
10.3000000000000007105  10.8000000000000007105  10.0000000000000000000
10.4000000000000003553  10.9000000000000003553  10.0000000000000000000
10.5000000000000000000  11.0000000000000000000  11.0000000000000000000
10.5999999999999996447  11.0999999999999996447  11.0000000000000000000
10.6999999999999992895  11.1999999999999992895  11.0000000000000000000
10.8000000000000007105  11.3000000000000007105  11.0000000000000000000
10.9000000000000003553  11.4000000000000003553  11.0000000000000000000
11.0000000000000000000  11.5000000000000000000  11.0000000000000000000

差异:第一次运行是累加误差的步骤,步骤为0.0999999999999996447,而第二次运行尽可能接近地重新计算,使得可以精确地达到10.5和11.0。

答案 1 :(得分:2)

这是输出,改为使用printf

printf("%.15f\t%.15f\t%.15f\n", a, a + 0.5, floor(a + 0.5));

现在很清楚:

10.000000000000000  10.500000000000000  10.000000000000000
10.100000000000000  10.600000000000000  10.000000000000000
10.199999999999999  10.699999999999999  10.000000000000000
10.299999999999999  10.799999999999999  10.000000000000000
10.399999999999999  10.899999999999999  10.000000000000000
10.499999999999998  10.999999999999998  10.000000000000000
10.599999999999998  11.099999999999998  11.000000000000000
10.699999999999998  11.199999999999998  11.000000000000000
10.799999999999997  11.299999999999997  11.000000000000000
10.899999999999997  11.399999999999997  11.000000000000000

答案 2 :(得分:0)

问题是由于舍入错误的累积。浮点数在内部表示不是整数,它们的值大多是近似值。因此,每次执行 a + =。1 a + .5 操作时,您都会累积舍入误差,结果就是您得到的结果。

您可以尝试不使用增量修改,而是使用以下表达式(通常它会在大范围内提供更好的结果):

a = 10. + .1 * i;

答案 3 :(得分:0)

问题是,正如其他人所指出的,你永远不会 实际上有10.5;你只有非常接近的东西 至10.5(但略小)。

作为一般规则,对于这类事情,你应该 向浮点值添加增量。你应该 只使用积分增量,并每次将其缩放到 浮点值:

for ( int i = 0; i != 10; ++ i ) {
    double aa = a + ( i / 10. );
    std::cout << aa << '\t' << aa + 0.5 << '\t' << floor( aa + 0.5 ) << std::endl;
}

这应该会给你想要的结果。

当然,如果你的例子只是一个测试......很多都取决于 如何计算舍入值。实际的舍入 你使用可能是适当的。或者,如果你知道的话 值应为0.1的倍数,您可以尝试执行此操作 算术缩放10,然后舍入结果,然后舍入到 10的倍数。