6

我正在使用数学库中的 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-mfpmath=sse-march=pentiumx64 两种情况。

4

2 回答 2

6

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

于 2012-05-30T00:45:02.537 回答
6

你还没有说你正在使用哪个编译器或架构,但假设gcc/ x86thenx86-64差异可能归结为默认情况下 gcc 在 32 位 x86 上使用 387 浮点指令,而它在 x86-64 上使用 SSE 指令.

387 个浮点寄存器是 80 位宽,而double64 位宽。这意味着使用 387 指令的中间结果可以具有更高的精度,这可能会导致四舍五入后的结果略有不同。(SSE2 指令在压缩的 64 位双精度上运行)。

有几种方法可以更改编译器的运行方式,具体取决于您的需要:

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