http://paste.ubuntu.com/26026112/
在做HDUOJ时发现的,输入153 153 计算时5的立方值int类型转换之后值为124,求问为什么在这会发生精度丢失,而我单独写一个pow(5,3)却不会?
32位平台下,int只有4个字节,能表示范围为(-2^31 ~2^31 - 1),看你计算的数是否有溢出
pow函数默认为double类型 8个字节,表示范围很大。
这真是一个神奇的现象。我在自己的机器上重现了一下,发现变量5的三次方有问题,其他的像4,6都没问题。那就看这个5的三次方是怎么回事吧:
如下是测试代码:
#include <stdio.h>
#include <math.h>
int main(){
int x = 5;
int y = (int)pow(x, 3);
volatile int z = (int)pow(5,3);
printf("%d\n",y);
printf("%d",z);
return 0;
}
运行结果:(windows7,MinGwin C)
124
125
好吧,问题已经复现...
开始调试,看看指令执行的时候发生了啥
注意对int z = (int)pow(5,3)
的汇编代码,它是直接将0x7d
也就是·125
直接赋值给变量z
。所以直接用像pow(5,3)这种立即数表达式
很有可能被编译器优化了,并没有调用pow
函数。
再看int y = (int)pow(x,3)
:x
是一个在栈中的局部变量,然后把它和另一个参数3
(在rodata段)放到浮点运算单元的栈上去,然后在取下来发生类型转换。就是fildl fldl fstpl
这几条指令,具体做了什么我也不甚了解。然后cal
l完了之后再对返回值进行类型转换,然后就特么...特么成了124...
好吧,我也就只能说到这了,那几个浮点指令没学过。知道大致执行过程也差不多了,我想我还是解决了pow(5,3)和pow(x,3)
的不一致问题。
首先,pow这个函数的参数和返回值,都是浮点类型,浮点函数在运算过程中,如果没有硬件浮点处理器支持的话,直接用cpu计算是很慢的,所以浮点函数pow内部并不是直接将5的3次方做两次乘法来计算,而是通过查表和某些特殊算法进行快速计算的,得到的结果也是近似值,比如124.99999,这样的值直接转换成整形,就变成124了,但作为浮点数,124.99999是没错的,所以浮点计算结果转整形是不精确的。
一般浮点直接转整型,需要做精度修正,比如你期望计算结果的精度损失是小于0.01(对于125来说,就是结果在124.99到125.01之间),那么计算得到的浮点结果,要先加上0.01,然后转整形就可以了,比如
y = (int)(pow(5,3) + 0.01);
pow这个函数的参数和返回值