今天调试一只程序时惊讶的发现,如下代码中get_dis()
的返回值在后续运算中会出现奇怪的结果(与同一数值比较时,一会儿偏大,一会偏小)
cpp
int sqr(int x) { return x*x; } double get_dis(int x1,int y1,int x2,int y2) { return sqrt(sqr(x1-x2)+sqr(y1-y2)); }
经过一番调试之后,发现解决方案有四个:
1、在get_dis()
前加上inline
修饰
2、将sqrt()
换成sqrtl()
函数
3、将数值强转成long double
类型后再传入sqrt()
中
4、关闭编译优化
想问一下,这四种解决方案都是什么原理。
另外,由于传入sqrt()
的数值都是小于200的小整数,在不开启编译优化的情况下返回的浮点数有十多位精度,在后续运算中连eps都不用加,然后就很好奇为啥开启浮点运算后精度会下降,于是书写了如下测试代码:
cpp
#include <cstdio> #include <cstdlib> #include <cmath> #include <ctime> using namespace std; void check(double r1,double r2) { long long *t1=(long long *)&r1; long long *t2=(long long *)&r2; printf("%I64d\n%I64d\n",*t1,*t2); if(*t1==*t2) puts("all bits are equal"); } int main() { srand(time(0)); while(true) { int x=rand()%200; double r1=sqrt(x); double r2=sqrtl(x); if(r1==r2) puts("r1==r2"); else if(r1<r2) puts("r1<r2"); else if(r1>r2) puts("r1>r2"); else { printf("%d\n%.20lf\n%.20lf\n",x,r1,r2); check(r1,r2); system("pause"); } } return 0; }
运行结果是
113
10.63014581273465000000
10.63014581273465000000
4622173858144899126
4622173858144899126
all bits are equal
请按任意键继续. . .
看完运行结果眼镜都要掉下来了,对于两个浮点数r1和r2,竟然出现!(r1==r2)&&!(r1<r2)&&!(r1>r2)
的情况,而且事实上这两个浮点数的二进制是相等的,求大神解答
更奇葩的是如上代码是在Win 7 x64 MinGW GCC 4.7.2 32-bit
下附加命令-march=native -msse4.2 -Ofast
编译得到的,而在其他环境下(Windows xp
或Ubuntu Server 14.04 LTS
或Ubuntu Desktop 10.10
)下,使用其他版本的g++
和其他的优化开关(比如单独的-O2
)下,虽然不会跳转到最后的那个else
分支去,但是确实会出现sqrt
和sqrtl
的返回值二进制相等但在比较时不等的情况,而在关闭编译优化的情况下,均能判断为r1==r2
,求教这是为什么
试了一下,用cl编译都是相等的,g++编译确实会有你说的情况。
但是,两个浮点数比较不是应该先做减法再用差值和较小的数比较么。。。