C++ 地板()行为异常
我需要一个函数将一个正的double取整为最接近的整数。我发现这是一种非常优雅的方式C++ 地板()行为异常,c++,integer,rounding,floor,C++,Integer,Rounding,Floor,我需要一个函数将一个正的double取整为最接近的整数。我发现这是一种非常优雅的方式 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; }
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;
}
双a=10.0;
对于(int i=0;i<10;i++){
cout通过添加0.1,确实可以添加略低于0.1的值
因此,将0.1乘以5与将0.5乘以一次并不相同;你不能精确地达到该值。再次将0.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));
}
}
区别:第一次运行的方法是累积误差和步长为0.099999999996447的方法,而第二次运行的方法是重新计算尽可能接近的a,使其能够精确地达到10.5和11.0。问题是由于舍入误差的累积。浮点数在内部表示为非整数s、 它们的值大多是近似值。因此,每次执行a+=.1和a+.5操作时,您都会累积舍入误差,结果就是您得到的结果
您可以尝试不看增量修改,而是看如何使用以下表达式(通常它在大规模上提供更好的结果):
以下是使用printf
的输出:
printf(“%.15f\t%.15f\t%.15f\n”,a,a+0.5,楼层(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
问题是,正如其他人所指出的,你从来没有
实际上有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;
}
for(int i=0;i!=10;++i){
双aa=a+(i/10.);
std::cout double和float(存储在浮点标准中的变量)只是近似值。精度不受精确小数的限制。关于浮点计算的很多细节:--网上也有不错的PDF版本。但GLGL的答案已经说明了这一点。10.5
实际上是10.499999999
左右。cout
打印得很漂亮,但floor知道10.999999999
不是11
10.5
是10.5
。机器浮点能够准确地表示它。问题是他从来没有10.5
。如果OP添加0.125
,他们会更好,因为1/8
可以准确地表示th。@PeterWood如果OP添加0.125
,那就更好了很可能他没有意识到可能有问题。@JamesKanze抱歉,是的,OP需要理解有问题。我想帮助他们思考为什么有些数字比其他数字更好。这是朝着正确方向迈出的一步,但这里的问题是,0.1
无法准确表示。使用10.+(i/10.
应给出正确的结果,但是:所有涉及的数字都可以准确表示,当i
为5
时,结果也可以准确表示。(当i
为1
时,结果仍不完全是10.1
。但对于舍入,我们只关心结果是否接近某个值。5
)需要注意的是,这里的舍入误差是在将.1
转换为浮点值时产生的。一个粗略的类比是int i=0;i+=3/2;i+=3/2;
。i
的结果值是2,而不是3。没有必要将问题与printf
混淆。只需将精度设置为std::cout
@JamesKanze-这个问题之前已经用C标记了。不过现在应该还是很清楚发生了什么。
a = 10. + .1 * i;
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
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;
}