使用64位或32位编译时的不同行为或sqrt

时间:2012-05-30 00:38:06

标签: c math 32bit-64bit sqrt mpfr

我正在使用数学库中的sqrt()函数,当我使用-m64构建64位时,我得到了正确的结果,但是当我构建32位时,我的行为非常不一致。

例如在64位

double dx = 0x1.fffffffffffffp+1023;
sqrt(dx); // => 0x1.fffffffffffffp+511
sqrt(0x1.fffffffffffffp+1023);// => 0x1.fffffffffffffp+511

(我相信这是正确的舍入结果,用mpfr验证)

但是在32位相同的输入值上,它表现不同。

double dx = 0x1.fffffffffffffp+1023;
sqrt(dx); // => 0x1.0p+512
sqrt(0x1.fffffffffffffp+1023); // => 0x1.fffffffffffffp+511

当在变量中传递相同的值时,我得到错误的结果。 我在每次调用之前和之后检查了舍入模式,并且所有都设置为舍入到最近。 是什么原因? 我在64位计算机上使用gcc 4.6,对于x86和x64两种情况,选项都是-mfpmath=sse-march=pentium

2 个答案:

答案 0 :(得分:6)

某些编译器,例如gcc,当他们看到在静态文字上执行某些数学库函数时,实际上在编译时计算该值,其中 - 与变量一样,它必须在运行。编译时值通常由编译器使用数学库(如MPFR,GNU MP等)计算,因此结果将更准确,或者至少从平台到平台尽可能准确。

答案 1 :(得分:6)

您还没有说过您正在使用哪个编译器或架构,但假设gcc / x86上有x86-64,那么差异很可能是因为默认情况下gcc使用32位x86上的387个浮点指令,而它在x86-64上使用SSE指令。

387个浮点寄存器为80位宽,而double为64位宽。这意味着使用387指令可以使中间结果具有更高的精度,这可能会在舍入后产生稍微不同的答案。 (SSE2指令在打包的64位双精度数上运行。)

根据您的需要,有几种方法可以改变编译器的运行方式:

  • 如果在x86版本中使用-ffloat-store选项,则只要在double变量中存储值,编译器就会丢弃额外的精度;
  • 如果您在x86版本上使用-mfpmath=sse选项,以及指定支持SSE2的架构的-msse2-march=开关,编译器将使用SSE指令进行浮点运算在x86-64上。但代码只能在支持SSE2的CPU上运行(Pentium-M / Pentium 4及更高版本)。
  • 如果在x86-64版本中使用-mfpmath=387选项,编译器将使用387指令进行浮点运算,就像在x86上一样。不建议这样做 - x86-64 ABI指定在SSE寄存器中传递浮点值,因此编译器必须使用此选项在387和SSE寄存器之间进行大量的混洗。