C++ pow()函数计算结果转换为整型时产生误差的问题

在LeetCode上在线刷题遇到的问题,简化为如下的问题:

int main(){
   long int x = 61453901951867000;
   long int y;
   cout<<fixed<<setprecision(15)<<5*(pow(10, 1))<<endl;  //输出50.000000000000000
   y = x + 5*(pow(10, 1));
   /*********************************************************************
   //其实等价于如下两行
   double j = 5*(pow(10, 1));
   y = x + j;          //如果是隐式类型转换会出问题,但如果强制类型转换就没有问题
   *********************************************************************/
   cout<<y<<endl;           //结果为61453901951867048
}

x是一个长整型,计算5*pow(10,1)的值并加到x上,返回的结果是61453901951867048,而非61453901951867050.

问题分析:

在整个计算过程中,隐式地发生了多次类型转换。

第一次:传入整数10和1并调用pow()函数,pow()函数默认的参数和返回值应该都是浮点数类型的(应该是double型),所以有一次从整型10和1转换为浮点数的过程。上述代码其实等价于:

第二次:计算完pow()函数后,返回了一个浮点数,并加到长整型数x上得到y。这个过程又发生了一次类型转换:从浮点数转换为长整型。也就是在这个转换过程中,发生了误差。可以看到上述两个代码的结果是一样的,输出的y值都是61453901951867048,而非61453901951867050.

如果是由于从double到长整型转换出的问题,那如果使用long double能否解决这一问题呢?试一下:

int main(){	
   long int x = 61453901951867000;
   long int y;
   
   cout<<fixed<<setprecision(18)<<j<<endl;
   y = x + (long double)(5*(pow(10, 1)));      //如果是隐式类型转换会出问题,但如果强制类型转换就没有问题
   /**********************************************************************
   //等价于如下两行
   long double j = 5*(pow(10, 1));
   y = x + j; 
   **********************************************************************/
   cout<<y<<endl;      //结果为61453901951867050
}

可见这一思路是对的,从long double到long int的隐式转换是正确的,如果只是double就会有问题。具体更细节的原因是什么,由于时间关系我没有作进一步研究,这玩意跟编译器也有关系,查起来也比较麻烦。所以我先记在这里,如果有哪位搞清楚了的可以写在评论区里。

那如果我们直接把pow()的计算结果强制转换为int又能否解决这一问题呢?试一下:

int main(){	
   long int x = 61453901951867000;
   long int y;
   y = x + (int)(5*pow(10, 1));       //这里用int或long int都可以,但如果数字变大则必须用long int才行
   /*********************************************************************
   int j = 5*(pow(10, 1));
   cout<<fixed<<setprecision(18)<<j<<endl;
   y = x + j;          //如果是隐式类型转换会出问题,但如果强制类型转换就没有问题
   *********************************************************************/
   cout<<y<<endl;      //结果为61453901951867050
}

这样的结果也是对的。

这个问题确实有点诡异,总结起来就是:

调用pow()函数返回的是double型结果,这个结果与一个长整型数long int相加的时候,有个隐式转换过程,在这一过程中产生了误差。如果把pow()的结果强制转换为long double再与long int相加,OK;如果把pow()的结果强制转换为int再与long int相加也OK。原因是隐式类型转换的时候出了问题(应该是从double转换为long int的时候数据位数不够导致的?我也不清楚)。

猜你喜欢

转载自blog.csdn.net/qq_41230365/article/details/81703192