7

私は最近、メモリに浮動小数点値を格納することについて読んでいました。そして、読んだ内容をテストするための小さなプログラムを作成しました。そして、Java が浮動小数点値を処理する方法に違いがあることに気付きました。

public class Test
{
   public static void main(String args[])
   {
     double a = 0.90;
     System.out.println(a);
     System.out.println(2.00-1.10);
   }
 }

上記のプログラムは印刷中です

0.9
0.8999999999999999

これらの両方のステートメントが同じ値を出力しないのはなぜですか? 一部の浮動小数点値を正確に表現できないことはわかっています。その場合、両方が同じ値を与える必要があります。

4

4 に答える 4

8

これらの両方のステートメントが同じ値を出力しないのはなぜですか?

結果は同じではありません。

一部の浮動小数点値を正確に表現できないことはわかっています。

したがって、演算の結果は、使用する値の表現エラーの量に依存する可能性があると想定する必要があります。

for (long l = 1; l <= 1e16; l *= 10) {
    double a = l + 2;
    double b = l + 1.1;
    System.out.println(a + " - " + b + " is " + (a - b));
}

値が大きくなるにつれて表現誤差が大きくなり、0.9 の結果と比較して大きくなります。

3.0 - 2.1 is 0.8999999999999999
12.0 - 11.1 is 0.9000000000000004
102.0 - 101.1 is 0.9000000000000057
1002.0 - 1001.1 is 0.8999999999999773
10002.0 - 10001.1 is 0.8999999999996362
100002.0 - 100001.1 is 0.8999999999941792
1000002.0 - 1000001.1 is 0.9000000000232831
1.0000002E7 - 1.00000011E7 is 0.900000000372529
1.00000002E8 - 1.000000011E8 is 0.9000000059604645
1.000000002E9 - 1.0000000011E9 is 0.8999999761581421
1.0000000002E10 - 1.00000000011E10 is 0.8999996185302734
1.00000000002E11 - 1.000000000011E11 is 0.899993896484375
1.000000000002E12 - 1.0000000000011E12 is 0.9000244140625
1.0000000000002E13 - 1.00000000000011E13 is 0.900390625
1.00000000000002E14 - 1.000000000000011E14 is 0.90625
1.000000000000002E15 - 1.0000000000000011E15 is 0.875
1.0000000000000002E16 - 1.0000000000000002E16 is 0.0

表現エラーが非常に大きくなると、操作は何もしません。

for (double d = 1; d < Double.MAX_VALUE; d *= 2) {
    if (d == d + 1) {
        System.out.println(d + " + 1 == " + (d + 1));
        break;
    }
}
for (double d = 1; d < Double.MAX_VALUE; d *= 2) {
    if (d == d - 1) {
        System.out.println(d + " - 1 == " + (d - 1));
        break;
    }
}

版画

9.007199254740992E15 + 1 == 9.007199254740992E15
1.8014398509481984E16 - 1 == 1.8014398509481984E16
于 2012-10-31T14:48:47.550 に答える
6

「0.90」を double に変換すると、結果は .9 に小さな誤差 e 0を加えたものになります。したがって、a.9+e 0に等しくなります。

「1.10」を double に変換すると、結果は 1.1 に小さな誤差 e 1を加えたものになるため、結果は 1.1+e 1になります。

これらの 2 つの誤差 e 0と e 1は、通常、互いに無関係です。簡単に言えば、異なる 10 進数は、2 進浮動小数点数からの距離が異なります。を評価する2.00-1.10と、結果は 2–(1.1+e 1 ) = .9–e 1になります。したがって、数値の 1 つは .9+e 0であり、もう 1 つは .9-e 1であり、それらが同じであると期待する理由はありません。

(As it happens in this case, e 0 is .00000000000000002220446049250313080847263336181640625, and e 1 is .000000000000000088817841970012523233890533447265625. Also, subtracting 1.1 from 2 introduces no new error, after the conversion of “1.1” to double, by Sterbenz' Lemma.)

追加の詳細:

バイナリでは、.9 は.11100110011001100110011001100110011001100110011001100 11001100…太字のビットはdouble に収まります。末尾のビットが収まらないため、数値はその時点で丸められます。これにより、正確な値 .9 と double として表される「.9」の値に違いが生じます。2 進数では、1.1 は1.00011001100110011001100110011001100110011001 10011001 です... 繰り返しますが、数値は丸められます。ただし、金額の丸めが異なることに注意してください。.9 の場合、1100 1100… は 1 0000 0000… に切り上げられ、その位置に 00110011… が追加されます。1.1 の場合、1001 1001 は 1 0000 0000… に切り上げられ、その位置に 01100110… が追加されます (太字のビットにキャリーが発生します)。そして、2 つの立場は異なります。1.1 は基数点の左側から始まるため、次のようになります。 1.[ここで52ビット][丸めが発生する場所]。.9 は基数ポイントの右側から始まるため、次のようになります: .[ここに 53 ビット][丸めが発生する場所]. したがって、1.1 の丸めは、00110011... ではなく 01100110... であることに加えて、.9 丸めの左に 1 ビット発生するため、2 倍になります。したがって、e 0を e 1とは異なるものにする 2 つの効果があります。丸められた末尾のビットが異なり、丸めが発生する場所が異なります。

于 2012-10-31T20:27:13.497 に答える
2

一部の浮動小数点値を正確に表現できないことは知っています

それがあなたの答えです (より正確には、Mark Byers が指摘したように、一部の 10 進数値は double として正確に表現できません)! 0.9 も 1.1 も double として表すことができないため、丸め誤差が発生します。

BigDecimal を使用して、さまざまな double の正確な値を確認できます。

public static void main(String args[]) {
    double a = 0.9d;
    System.out.println(a);
    System.out.println(new BigDecimal(a));
    double b = 2d - 1.1d;
    System.out.println(b);
    System.out.println(new BigDecimal(2.0d));
    System.out.println(new BigDecimal(1.1d));
    System.out.println(new BigDecimal(b));
}

出力:

0.9
0.90000000000000002220446049250313080847263336181640625
0.8999999999999999
2
1.100000000000000088817841970012523233890533447265625
0.899999999999999911182158029987476766109466552734375
于 2012-10-31T14:49:50.300 に答える
1

あなたの推論は、 0.9 を a で正確に表すことができない場合でも、 2.0 - 1.1doubleとまったく同じdouble値を持つ必要があるため、同じ出力値になるということです。doubleこれがエラーです。この減算では、"0.9" (または正確な値 0.9) で表されるは得られません。

于 2012-10-31T14:49:40.363 に答える