我正在使用数学库中的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
。
答案 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位双精度数上运行。)
根据您的需要,有几种方法可以改变编译器的运行方式:
-ffloat-store
选项,则只要在double
变量中存储值,编译器就会丢弃额外的精度; -mfpmath=sse
选项,以及指定支持SSE2的架构的-msse2
或-march=
开关,编译器将使用SSE指令进行浮点运算在x86-64上。但代码只能在支持SSE2的CPU上运行(Pentium-M / Pentium 4及更高版本)。-mfpmath=387
选项,编译器将使用387指令进行浮点运算,就像在x86上一样。不建议这样做 - x86-64 ABI指定在SSE寄存器中传递浮点值,因此编译器必须使用此选项在387和SSE寄存器之间进行大量的混洗。