C++:双精度,精度,虚拟机和GCC

Jar*_*sie 21 c++ compiler-construction optimization double-precision vm-implementation

我有以下代码:

#include <cstdio>
int main()
{
   if ((1.0 + 0.1) != (1.0 + 0.1))
      printf("not equal\n");
    else
      printf("equal\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

当使用gcc(4.4,4.5和4.6)使用O3编译并本机运行(ubuntu 10.10)时,它会打印预期的"相等"结果.

但是,当如上所述编译并在虚拟机(ubuntu 10.10,virtualbox image)上运行时,它输出"不相等"的情况下相同的代码 - 这是O3和O2标志设置但不是O1及以下时的情况.当使用clang(O3和O2)编译并在虚拟机上运行时,我得到了正确的结果.

我理解1.1无法使用double正确表示,我读过"每个计算机科学家应该知道浮点算术的内容"所以请不要指向我那里,这似乎是GCC做的某种优化不知何故似乎在虚拟机中不起作用.

有任何想法吗?

注意:C++标准说在这种情况下类型提升是依赖于实现的,可能是GCC使用更精确的内部表示,当应用不等式测试时,它是否成立 - 由于额外的精度?

UPDATE1:以上修改上面的代码,现在得到了正确的结果.在某些时候,无论出于何种原因,GCC都会关闭浮点控制字.

#include <cstdio>
void set_dpfpu() { unsigned int mode = 0x27F; asm ("fldcw %0" : : "m" (*&mode)); 
int main()
{
   set_dpfpu();
   if ((1.0 + 0.1) != (1.0 + 0.1))
      printf("not equal\n");
    else
      printf("equal\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

UPDATE2:对于那些询问代码的const表达性质的人,我已经改变了如下,并且在使用GCC编译时仍然失败. - 但我认为优化器也可能将以下内容转换为const表达式.

#include <cstdio>
void set_dpfpu() { unsigned int mode = 0x27F; asm ("fldcw %0" : : "m" (*&mode)); 
int main()
{
   //set_dpfpu();  uncomment to make it work.
   double d1 = 1.0;
   double d2 = 1.0;  
   if ((d1 + 0.1) != (d2 + 0.1))
      printf("not equal\n");
    else
      printf("equal\n");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

UPDATE3解决方案:将virtualbox升级到版本4.1.8r75467解决了该问题.然而,他们仍然是一个问题,那就是:为什么clang构建起作用.

aho*_*fer 10

更新:请参阅此文章如何处理浮点计算中的过度精度? 它解决了扩展浮点精度的问题.我忘记了x86中的扩展精度.我记得一个应该是确定性的模拟,但是在Intel CPU上比在PowePC CPU上给出了不同的结果.原因是英特尔扩展的精密架构.

该网页讨论了如何将Intel CPU投入双精度舍入模式:http://www.network-theory.co.uk/docs/gccintro/gccintro_70.html.


virtualbox是否保证其浮点运算与硬件的浮点运算相同?通过Google快速搜索,我无法找到类似的保证.我也没有发现vituralbox FP ops符合IEEE 754的承诺.

虚拟机是模拟器,可以模拟特定的指令集或体系结构,并且大多数成功.然而,它们只是模拟器,并且受制于它们自己的实现怪癖或设计问题.

如果您还没有,请发布问题forums.virtualbox.org并查看社区对此的评价.


Voo*_*Voo 5

是的,这是非常奇怪的行为,但实际上可以很容易地解释:

在x86浮点寄存器内部使用更高的精度(例如80而不是64).这意味着计算1.0 + 0.1将在寄存器中以更高的精度计算(并且因为在所有那些将要使用的额外位,所以1.1不能精确地以二进制表示).仅当将结果存储到存储器时才会被截断.

这意味着什么很简单:如果你将从内存加载的值与在寄存器中新计算的值进行比较,你会得到一个"不相等"的回,因为一个值被截断而另一个没有.因此,这与VM /无VM无关,它只取决于编译器生成的代码,这些代码很容易随着我们看到的那样波动.

将它添加到不断增加的浮点惊喜列表中..

  • 我没有提出这个答案,因为我希望在将文字发送到代码时在编译时截断"0.1".除非结果需要比输入更多的位,否则以更高的精度进行数学运算并不重要.在反射时,添加可能需要一个或两个额外的位,所以我认为这是可能的. (3认同)