【问题标题】:Double and float may have the same precision?双精度和浮点数可能具有相同的精度?
【发布时间】:2015-05-17 23:05:00
【问题描述】:

我必须实现一个程序来计算 float 和 double 的机器 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 和 -mpc80 编译我的程序,结果如下:
- mpc32 浮点 23,双 23
-mpc64 浮点 52,双 52
-mpc80 浮点 63,双 63
我也在 64 位机器上尝试了这些编译选项,但结果总是 23 和 52。
我知道 float 是单精度,而 double 是双精度,但我的 32 位虚拟机的编译器可能对 float 和 double 都使用 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) 在循环中被评估为 long double测试。

【问题讨论】:

  • 它是特定于编译器和实现的。阅读floating-point-gui.de
  • 可能和你的虚拟机处理浮点指令有关
  • 在您的 x86 机器上 FLT_EVAL_METHOD 为 2,但在 x86_64 上为 0。有关说明,请参见链接站点。
  • 试试printf("%zu\n",sizeof(float));。这不会告诉你什么是 epsilon,但会告诉你 float 在虚拟机上有多少字节。
  • 您可能会被浮点寄存器中保存的值所欺骗。

标签: c floating-point double-precision


【解决方案1】:

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

浮点数 63 双63

简而言之,当编译器将FLT_EVAL_METHOD 定义为2 时,就像在您的32 位虚拟机上一样,浮点表达式和常量are evaluated to 的精度为long double,无论它们的类型如何, 并且仅对左值的赋值和显式转换将计算值从 long double 舍入到实际的浮点类型。表达式1+(tmp=tmp/2) 的顶层没有这样的结构,因此加法运算的精度为long double。

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

【讨论】:

  • tmp 是左值,所以tmp = tmp/2 是左值的赋值,应该强制舍入。 assignment-expression 的结果具有左侧操作数的类型(不是右侧的原始结果)
  • @MattMcNabb 是的,但是float 有足够的范围来表示long double 的机器 epsilon(实际上它在 OP 的程序中确实如此)。重要的是1+…中的+是双扩展1和tmp的值转换为双扩展之间的双扩展加法。
  • @MattMcNabb 我已经改写了你所指的句子。
  • 您能否更详细地解释一下(也许使用标准参考资料)为什么“顶级”作业与其他作业不同?
  • @MattMcNabb 在1.0f + (y = e) 中,当FLT_EVAL_METHOD > 0 仅适用于y = e 时,分配和强制转换的规则会移除额外的精度。加法仍然以FLT_EVAL_METHOD 的值所暗示的额外精度完成。这就是我的意思。
猜你喜欢
  • 2018-02-23
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2018-07-06
  • 1970-01-01
  • 2020-05-28
  • 2011-04-06
相关资源
最近更新 更多