代码之家  ›  专栏  ›  技术社区  ›  LolloFake

双精度和浮点精度可能相同?

  •  3
  • LolloFake  · 技术社区  · 9 年前

    我必须实现一个程序来计算机器epsilon 浮动 双重的 .
    我编写了这些函数:

    int feps(){
        //machine epsilon for float
        float tmp=1;
        int d=0;
        while(1+(tmp=tmp/2)>1.0f)d++;
        return d;
    }
    
    int deps(){
        //machine epsilon for double
        double tmp=1;
        int d=0;
        while(1+(tmp=tmp/2)>1.0)d++;
        return d;
    }
    


    笔记 :
    64位机器编译器gcc 4.9.1目标:x86_64-linux-gnu
    32位机器编译器gcc 4.8.2目标:i686 linux gnu

    我在一个 64 钻头机,结果是:
    浮动23
    双52
    正如我所料,然后我在 32 比特虚拟机,结果非常奇怪:
    浮动63
    双63
    我还尝试用 -mpc32型 , -mpc64型 -英里80 结果如下:
    -mpc32型 浮点23,双23
    -mpc64型 浮动52,双52
    -英里80 浮点63,双63
    我也在64位机器中尝试了这些编译选项,但结果总是23和52。
    我知道float是单精度的,double是双精度的,但我的32位虚拟机的编译器可能使用了 binary80 浮点数和双精度的格式?

    我很确定我的代码是正确的,所以我认为问题与编译器有关,或者是更微妙的问题。
    我花了一整天的时间搜索浮点的相关信息,也读了一些MMX/SSE指令的相关内容,但我不太理解,还有一些关于x87 FPU的内容可能会产生一些问题。


    更新:
    我要感谢所有帮助我的人,我设法获得了32位虚拟机中float和double的真正epsilon值,这是代码:
    int feps(){
        float tmp=1;
        int d=0;
        float tmp2=1;
        do{
            tmp2=1+(tmp=tmp/2);
            d++;
        }while(tmp2>1.0f);
        return d-1;
    }
    
    int deps(){
        double tmp=1;
        int d=0;
        double tmp2=1;
        do{
            tmp2=1+(tmp=tmp/2);
            d++;
        }while(tmp2>1.0);
        return d-1;
    }
    

    正如您所看到的,我们需要将中间结果放入一个变量中,这样我们可以防止1+(tmp=tmp/2)被计算为 长双 在循环测试中。

    1 回复  |  直到 9 年前
        1
  •  3
  •   Pascal Cuoq    9 年前

    在32位平台上,ABI约束使得使用历史浮点寄存器更加简单;因此,编译器定义 FLT_EVAL_METHOD 作为2。这就是你如何获得:

    Float 63
    Double 63
    

    简而言之,当 FLT_EVAL_方法 由编译器定义为2,在32位虚拟机上也是如此,浮点表达式和常量 are evaluated to 精度 long double ,而不考虑其类型,并且仅对lvalues赋值和对计算值的显式转换 长双 转换为实际的浮点类型。在表达式的顶层没有这样的构造 1+(tmp=tmp/2) ,因此相加的精度为 长双 .

    这两个帖子 series 显示了一些示例 FLT_EVAL_方法 除了你的,你也会有所不同。根据J.s.Myers的解释,GCC的行为是确定性的。Clang的行为是不确定性的(当时和现在),开发人员对改进编译器的这种模式几乎没有兴趣。