次の C# コードを検討してください。
double result1 = 1.0 + 1.1 + 1.2;
double result2 = 1.2 + 1.0 + 1.1;
if (result1 == result2)
{
...
}
result1 は常に result2 と等しくなければなりませんよね?問題は、そうではないということです。result1 は 3.3 で、result2 は 3.3000000000000003 です。唯一の違いは、定数の順序です。
double は、丸めの問題が発生する可能性がある方法で実装されていることを知っています。絶対精度が必要な場合は、代わりに小数を使用できることを認識しています。または、if ステートメントで Math.Round() を使用できること。私は、C# コンパイラが何をしているかを理解したい単なるオタクです。誰でも教えてもらえますか?
編集:
これまでに浮動小数点演算について読むことを提案したり、CPU が double を処理する方法に固有の不正確さについて話したりしたすべての人に感謝します。しかし、私の質問の要点はまだ答えられていないと感じています。それを正しく表現していないのは私のせいです。次のように言いましょう。
上記のコードを分解すると、次の操作が行われると予想されます。
double r1 = 1.1 + 1.2;
double r2 = 1.0 + r1
double r3 = 1.0 + 1.1
double r4 = 1.2 + r3
上記の加算のそれぞれに丸め誤差 (番号 e1..e4) があると仮定しましょう。したがって、r1 には丸め誤差 e1 が含まれ、r2 には丸め誤差 e1 + e2 が含まれ、r3 には e3 が含まれ、r4 には e3 + e4 が含まれます。
さて、丸め誤差がどのように発生するのか正確にはわかりませんが、e1 + e2 が e3 + e4 に等しいと予想していました。明らかにそうではありませんが、それは私にはどういうわけか間違っているようです。もう 1 つのことは、上記のコードを実行すると、丸めエラーが発生しないことです。それが、CPU ではなく、C# コンパイラが奇妙なことをしていると私に思わせる理由です。
私は多くのことを尋ねていることを知っています。おそらく、誰もが与えることができる最良の答えは、CPU 設計で PHD を実行することですが、私は尋ねたいと思いました。
編集 2
元のコード サンプルの IL を見ると、これを行っているのは CPU ではなくコンパイラであることは明らかです。
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 1
.locals init (
[0] float64 result1,
[1] float64 result2)
L_0000: nop
L_0001: ldc.r8 3.3
L_000a: stloc.0
L_000b: ldc.r8 3.3000000000000003
L_0014: stloc.1
L_0015: ret
}
コンパイラは私のために数字を合計しています!