我需要一个函数将正双精简到其最接近的整数。潜伏着我,发现这种非常优雅的方式
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
那是什么意思?
感谢 问候 卢卡
答案 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的倍数。