3

我需要一个函数将正双精度数舍入到最接近的整数。潜伏的奥伦德我发现了这种非常优雅的方式

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

4 回答 4

3

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

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


交流节目如

#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,而第二次运行重新计算 a 尽可能接近,从而可以精确地达到 10.5 和 11.0。

于 2013-03-13T09:25:58.633 回答
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
于 2013-03-13T09:35:31.023 回答
0

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

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

a = 10. + .1 * i;
于 2013-03-13T09:31:05.987 回答
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 的倍数。

于 2013-03-13T09:46:29.173 回答