2

OK、pow 関数とその結果を int にキャストすることについて多くの質問があったことは知っていますが、この少し具体的な質問に対する答えが見つかりませんでした。

OK、これはCコードです:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    int i = 5;
    int j = 2;

    double d1 = pow(i,j);
    double d2 = pow(5,2);
    int i1 = (int)d1;
    int i2 = (int)d2;
    int i3 = (int)pow(i,j);
    int i4 = (int)pow(5,2);

    printf("%d %d %d %d",i1,i2,i3,i4);

    return 0;
}

これが出力です: "25 25 24 25". pow への引数がリテラルでない 3 番目のケースでのみ、おそらく丸め誤差が原因で間違った結果になることに注意してください。明示的なキャストがなくても同じことが起こります。この4つのケースで何が起こるか誰か説明できますか?

Windows 7 で CodeBlocks を使用し、それに付属の MinGW gcc コンパイラを使用しています。

4

4 に答える 4

4

演算の結果は、pow25.0000 プラスまたはマイナスの丸め誤差です。丸め誤差が正またはゼロの場合、整数への変換の結果は 25 になります。丸め誤差が負の場合、結果は 24 になります。どちらの答えも正しいです。

内部で発生する可能性が最も高いのは、高精度の 80 ビット FPU 値が直接使用されている場合と、結果が FPU からメモリに (64 ビット double として) 書き込まれている場合です。次に読み戻します (わずかに異なる 80 ビット値に変換します)。これにより、最終結果に微視的な違いが生じる可能性があります。これは、25.0000000001 を 24.999999997 に変更するのに必要なすべてです。

もう 1 つの可能性は、コンパイラが に渡された定数を認識powし、計算自体を実行して、 への呼び出しを結果に置き換えていることですpow。コンパイラは、内部の任意精度の数学ライブラリを使用する場合もあれば、異なるライブラリを使用する場合もあります。

于 2013-06-12T10:29:19.200 に答える
2

これは、次の 2 つの問題が組み合わさって発生します。

  • 使用している実装はpow高品質ではありません。pow(5, 2)多くの場合、浮動小数点演算は必然的に概算になりますが、適切な実装では、次のような単純なケースで正確な結果が返されるように注意が払われます。使用powしている は、25 未満で、0 より大きく 2 –49以下の結果を返しています。たとえば、 25–2 -50を返す場合があります。
  • 使用している C 実装は、64 ビット浮動小数点形式を使用する場合もあれば、80 ビット浮動小数点形式を使用する場合もあります。数値が 80 ビット形式で保持されている限り、pow返された完全な値が保持されます。この値を整数に変換すると、24 になります。これは、値が 25 未満であり、整数への変換で切り捨てられるためです。丸まらない。数値が 64 ビット形式に変換されると、丸められます。浮動小数点形式間の変換は丸められるため、結果は最も近い表現可能な値 25 に丸められます。その後、整数への変換は 25 を生成します。

コンパイラは、ある意味で「便利」な場合はいつでもフォーマットを切り替えることができます。たとえば、80 ビット形式のレジスタの数は限られています。それらがいっぱいになると、コンパイラは一部の値を 64 ビット形式に変換してメモリに格納することがあります。コンパイラは、実行時ではなくコンパイル時に式を再配置したり、それらの一部を実行したりすることもあり、これらは実行される演算と使用される形式に影響を与える可能性があります。

C の実装で浮動小数点形式が混在していると厄介です。これは、通常、形式間の変換がいつ発生するかをユーザーが予測または制御できないためです。これにより、簡単に再現できない結果が得られ、ソフトウェアの数値プロパティの導出または制御が妨げられます。C 実装は、全体で単一の形式を使用してこれらの問題の一部を回避するように設計できますが、C 実装は明らかにそのように設計されていません。

于 2013-06-12T15:35:56.407 に答える