recherche

Maison  >  Questions et réponses  >  le corps du texte

c++ - g++启用编译优化后浮点异常

今天调试一只程序时惊讶的发现,如下代码中get_dis()的返回值在后续运算中会出现奇怪的结果(与同一数值比较时,一会儿偏大,一会偏小)

cppint 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 xpUbuntu Server 14.04 LTSUbuntu Desktop 10.10)下,使用其他版本的g++和其他的优化开关(比如单独的-O2)下,虽然不会跳转到最后的那个else分支去,但是确实会出现sqrtsqrtl的返回值二进制相等但在比较时不等的情况,而在关闭编译优化的情况下,均能判断为r1==r2求教这是为什么

大家讲道理大家讲道理2804 Il y a quelques jours595

répondre à tous(1)je répondrai

  • 黄舟

    黄舟2017-04-17 11:51:19

    试了一下,用cl编译都是相等的,g++编译确实会有你说的情况。

    但是,两个浮点数比较不是应该先做减法再用差值和较小的数比较么。。。

    double r1, r2;
    ...
    double r3 = r1 - r2;
    if(r3 > 0.0000001) cout << "r1>r2";
    else if(r3 < -0.0000001) cout << "r1<r1";
    else cout << "r1=r2";
    

    répondre
    0
  • Annulerrépondre