18

なぜこれが正しいのですか?Java は、C や Java の Math.pow メソッドと比べて、2 つの浮動小数点数を乗算する際にわずかな差異を伴う結果を生成するように見えます。

ジャワ:

float a = 0.88276923;

double b = a * a;   // b becomes 0.779281497001648  <---- what???
b = Math.pow(a,2);  // b becomes 0.7792815081874238

子:

float a = 0.88276923;

double b = a * a;   // b becomes 0.7792815081874238
pow(a,2);           // b becomes 0.7792815081874238

更新: Ed S. のコメントによると、コンパイラによって C の動作が変わることもわかりました。gcc を使用すると、Java の動作と一致するように見えます。Visual Studio を使用すると (ターゲット プラットフォームによって異なります)、上記の結果または Java で見られる結果を生成できます。うーん。

4

2 に答える 2

16

pstと真実性がすでに賢明に指摘されているように、Cは乗算のfloatにをaに昇格させています。実際には、スタックにプッシュされると、80ビットの拡張精度値にプロモートされます。これがアセンブラー出力です(VS2005 x86 C89)double

    double b = a * a;
00411397  fld         dword ptr [a] 
0041139A  fmul        dword ptr [a] 
0041139D  fstp        qword ptr [b] 

FLD命令

FLD命令は、32ビット、64ビット、または80ビットの浮動小数点値をスタックにロードします。この命令は、32ビットと64ビットのオペランドを80ビットの拡張精度値に変換してから、値を浮動小数点スタックにプッシュします。


興味深いことに、x64をターゲットにビルドすると、movss命令が使用さ0.779281497001648れ、結果としての値、つまりJavaの例に表示されている値が得られます。試してみる。

于 2012-04-17T23:50:07.780 に答える
10

Java の目的

double b = a * a;

a * aは最初に(32 ビット) として乗算され、に代入するときにfloat結果を (64 ビット) に変換します。doubleb

b = Math.pow(a,2);

a最初に (64 ビット) に変換し (doubleのパラメーターが でMath.powあるためdouble, double)、次にそれを 2 乗します。

(私にとって)不可解なのは、Cが最初にa's をキャストするように見える理由です。double

double b = a * a;

それは規格にあるのですか?

編集: Cが数値に特定の実装(使用されるビット数に関して)を必要としないことを漠然と覚えています...それはここで起こっていることですか?あなたfloatのは64ビットですか?(Java では、afloatは常に 32 ビットであり、adoubleは常に 64 ビットです)。

編集: Ed S. の回答と、異なるコンパイラが異なる結果を与えるというマークのコメントの両方は、C の結果が実装固有およびアーキテクチャ固有であることを示しています。

于 2012-04-17T23:41:26.140 に答える