2017-08-17 11 views
1

acos(double) x64とx32 Visual Studioで異なる結果が得られます。 x64の上x64とx32でacos(double)の結果が異なるVisual Studio

printf("%.30g\n", double(acosl(0.49990774364240564))); 
printf("%.30g\n", acos(0.49990774364240564)); 

1.0473040763868076 x32の上
:SSEとlinux4.4のx32のおよびx64上の1.0473040763868078

が有効:1.0473040763868078

はVSx64 acos()が、結果として私に1.0473040763868078を与えるようにする方法があります?

+0

\プロジェクト\ ConsoleApplication1 \リリース> ConsoleApplication1.exe 1.04730407638680778070749965991 1.04730407638680778070749965991 C:\プロジェクト\ ConsoleApplication1 \ x64の\リリース> ConsoleApplication1.exe 1.04730407638680755866289473488 1.04730407638680755866289473488 – Artimosha

+0

ある答えはどのようなものあなたが探している? C++標準ではこれが許されています(+ - *とsqrtは、仮数の最後のビットまで正確に丸められた結果を生成するために必要です)。では、異なるライブラリ実装のasmレベルの詳細を尋ねていますか?確定的なFP計算は非常に難しい問題であり、AFAIKは一般的にもっと複雑なライブラリ関数を避けなければなりません。それでも、異なるコンパイラと異なるアーキテクチャは、C++で非常に難しくしています。 –

+0

@Artimosha:なぜあなたにとって問題なのですか? IEEE754は、超越関数の最も正確な結果を保証するものではありません。 4つの基本操作とsqrtに対してのみ保証されています。 – geza

答えて

2

TL:DR:これは正常であり、合理的に変更することはできません。


32ビットライブラリは、すべての操作の後に64ビットdoubleに四捨五入避け、その一時のためのx87レジスタに80ビットのFP値を使用することができます。 SSEを使用するための独自のコードをコンパイルしてもライブラリ内の内容やライブラリにデータを渡すための呼び出し規約は変更されませんが、スタック上のメモリにはdoublefloatが32ビットで渡されるためライブラリはSSE2またはx87で自由にロードできますが、SSE以外のコードでライブラリを使用することが不可能でない限り、FP値をxmmレジスタに渡すことによるパフォーマンス上の利点はありません)。

It's異なる順序の操作を使用し、その途中で異なる一時的な時間を生み出すため、単純に違う可能性もあります。彼らが別々に手書きでasmで書かれていない限り、それはあまりあり得ません。それらが同じCソース(「安全でない」FP最適化なし)から構築されている場合、コンパイラはFP数学のこの非結合的な動作のために物事を並べ替えることができません。 (Linuxで使用)


のglibcのlibmのは、典型的には、その32ビットと64ビットの両方のための仮数部の最後のビットに出あなたに正しく丸められた結果を与えるので、スピードの上に精度を優先する。 IEEE FP標準では、基本演算(+ - */FMAとFP剰余)が仮数の最後のビットに "正しく丸められる"ことだけが要求されます。 (つまり、最大で0.5 ulpの丸め誤差)。 (calcの正確な結果は1.047304076386807714...です。double(通常のコンパイラを使用しているx86の場合)はIEEE754 binary64なので、内部的には仮数と指数はbase2になります。ただし、十分な十進数を印刷すると、本当にあなたは、彼らがそれを超えゼロじゃない場合にはより多くの桁数を印刷する必要がありますが、その...7714は、...78に切り上げなければならない。私はちょうどそれが...78000だと仮定しています。)

をだから、マイクロソフトの64ビット・ライブラリの実装は1.0473040763868076を生産してありますそれを使わない以外は、あなたができることはほとんどありません。。 (たとえば、独自のacos()実装を見つけて使用してください。)しかし、FP決定論は、というハードです。Does any floating point-intensive code produce bit-exact results in any x86-based architecture?を参照してください。あなた自身を単一のコンパイラに限定すれば、複雑なライブラリ関数、例えばacos()を避ければ可能です。

x87を使用してx87の精度設定を変更すると、64ビットバージョンと同じ値になるように32ビットライブラリバージョンを取得できる場合があります。 SSE2は、64ビットのdoubleと32ビットのfloatの命令を別々に持ち、すべての命令の後に常にラウンドするため、精度の向上をもたらす設定を変更することはできません。 (あなたはSSE丸めモードを変更することができ、それは良い方法で結果を変更しませんが!)

も参照してください:

2

精度限界に達している可能性があります。 Double precision is approximately 16 digits。その後、数字が有効であるという保証はありません。その場合は、double型を別のものに変更することを除いて、この動作を変更することはできず、より高い精度がサポートされます。

など。あなたのマシンとコンパイラがextended 80 bit doubleまたは128ビットQuadrupleをサポートしている場合は、長いdouble型を使用してください(たとえばマシンに依存する場合はhereを参照してください)。

+2

実際の問題は、計算中の一時変数の精度です。これは、x87命令を使用する場合、32ビットライブラリ関数では80ビットになります。 AMD64にはSSE2が含まれており、デフォルトで使用されているため、64ビット版ではすべての手順で64ビットの「double」しか使用されません。どちらのバージョンでもSSE2を使用していても、同じ操作を異なる順序で実行するだけで、異なる結果が生成される可能性があります(中間体を別々に丸める)。しかし、これは、安全でないFP最適化を有効にしてライブラリを構築した場合にのみ発生します。 (FP数学は連想ではなく、コンパイラ/標準はこれを知っています)。 –

+0

@PeterCordes 64ビットマシンでは64ビットの2倍しか使用できないことはわかりませんでした。良い点、ありがとう。 – user1810087

1

私はずっとあなたはそれについて行うことができますがないことを同意しない。

たとえば、浮動小数点モデルのコンパイラオプションを変更してみることができます。ここで

は異なる浮動小数点モデルとの私の結果は、(ノート/fp:preciseがデフォルトです)、次のとおりです。

/fp:precise 1.04730407638680755866289473488 
/fp:strict 1.04730407638680755866289473488 
/fp:fast  1.04730407638680778070749965991 

だから、あなたが/fp:fastを探しているようです。それが最も正確な結果を示すかどうかはまだ分かりません。 C:VS2015で私のために再現

+0

私の間違い;私は、VC++に異なるFP設定のための数学ライブラリの実装が異なることは知らなかった。私の非常に簡単な 'calc'によるテストが正しければ、皮肉なことに'/fp:fast'の結果は正しく丸められ、他のものは* this *の特定の入力値ではありません。 –

関連する問題