我必须实现一个程序来计算 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 Float 23,Double 23
-mpc64 Float 52,Double 52
-mpc80 Float 63,Double 63
我也在64位机器上尝试了这些编译选项,但结果总是23和52
我知道float是单精度而double是双精度但我的32位虚拟机的编译器可能使用binary80格式用于float和double吗?
我非常确定我的代码是正确的,所以我认为这个问题与编译器或更微妙的东西有关。 我花了一整天时间搜索有关浮点数的信息,并且我已经阅读了有关MMX / SSE指令的内容,但我对此并不了解,而且x87 FPU可能会产生一些问题。< / p>
<小时/> 的更新
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 测试。
答案 0 :(得分:3)
在32位平台上,ABI约束使得使用历史浮点寄存器变得更加简单;因此,编译器将FLT_EVAL_METHOD
定义为2.这就是你得到的结果:
Float 63 Double 63
简而言之,当编译器将FLT_EVAL_METHOD
定义为2时,就像32位虚拟机上的情况一样,浮点表达式和常量are evaluated to的精度为long double
无论它们的类型如何,只有lvalues和显式转换的赋值将计算值从long double
四舍五入到实际的浮点类型。表达式1+(tmp=tmp/2)
的顶层没有这样的结构,因此添加的计算精度为long double
。
此两个帖子series显示了一些示例,其中FLT_EVAL_METHOD
除了您之外还有所不同。 GCC的行为是确定性的,并且根据J.S.Myers的解释。 Clang的行为是不确定的(当时和现在),开发人员对改进他们的编译器模式没什么兴趣。