6

-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すると、gcc はデフォルトで 32 ビット x86 では 387 個の浮動小数点命令を使用するのに対し、x86-64 では SSE 命令を使用するという事実に違いがある可能性があります。 .x86x86-64

387 個の浮動小数点レジスタは 80 ビット幅ですdoubleが、64 ビット幅です。これは、387 命令を使用して中間結果の精度を高めることができることを意味し、丸め後にわずかに異なる結果になる可能性があります。(SSE2 命令は、パックされた 64 ビット double で動作します)。

必要に応じて、コンパイラの動作方法を変更する方法がいくつかあります。

  • x86 ビルドでこのオプションを使用すると、コンパイラは変数-ffloat-storeに値を格納するたびに余分な精度を破棄します。double
  • -mfpmath=ssex86 ビルドでオプションを使用する場合、-msse2または-march=SSE2 をサポートするアーキテクチャを指定するスイッチと共に、コンパイラは x86-64 と同様に浮動小数点に SSE 命令を使用します。ただし、コードは SSE2 をサポートする CPU (Pentium-M / Pentium 4 以降) でのみ実行されます。
  • -mfpmath=387x86-64 ビルドでこのオプションを使用すると、コンパイラは x86 と同様に浮動小数点に 387 命令を使用します。ただし、これはお勧めしません。x86-64 ABI では、浮動小数点値が SSE レジスタで渡されることが指定されているため、コンパイラは、このオプションを使用して 387 レジスタと SSE レジスタの間で多くのシャッフルを行う必要があります。
于 2012-05-30T01:10:55.323 に答える