2012-03-12 12 views
1

可能性の重複:
Double precision problems on .NET
Double calculation producing odd result予測不可能なダブル

私は二重の値0.2の内部represantationが0.199999のようなものであることを知っています。しかし、次のコードはまだ私を混乱させる。

CODE:

public static void main(String[] args) { 
    double d= 0.3d; 
    double f= 0.1d; 
    System.out.println(d+f); 
    System.out.println(d*f); 
    System.out.println(d); 
    System.out.println(f); 
    System.out.println(d-f); 
    System.out.println(d/f); 
    System.out.println((d-f)*(d-f)); 
} 

がOUTPUT:

0.4 
0.03 
0.3 
0.1 
0.19999999999999998 
2.9999999999999996 
0.039999999999999994 

実際に何が起こっていますか?さらに、乗算はうまくいくが、減算、除算は行われない。誰でも詳細を教えてくださいなぜ、加算は減算と異なるのですか?

+10

小数を示し意味#記号を使用して非常にうまくこれを行います最も頼まれた人のひとりでなければならないコンピューティングの質問に答えました。 :P –

+1

...とon Stackoverflow – Thilo

+6

あなたの質問の最初の文は答えですね! – assylias

答えて

4

簡単な答えは、表現エラーと浮動小数点演算の丸め誤差があることです。 toString()は表現エラーについて「知っている」ので、丸めエラーがなければ表示されません。しかし、丸め誤差が大きすぎる場合、あなたは行います。

解決方法は、BigDecimalを使用するか、結果を丸めることです。


BigDecimalを使用すると、実際に実際の値が表示されます。

double d = 0.3d; 
double f = 0.1d; 
System.out.println("d= " + new BigDecimal(d)); 
System.out.println("f= " + new BigDecimal(f)); 
System.out.println("d+f= " + new BigDecimal(d + f)); 
System.out.println("0.4= " + new BigDecimal(0.4)); 
System.out.println("d*f= " + new BigDecimal(d * f)); 
System.out.println("0.03= " + new BigDecimal(0.03)); 
System.out.println("d-f= " + new BigDecimal(d - f)); 
System.out.println("0.2= " + new BigDecimal(0.2)); 
System.out.println("d/f= " + new BigDecimal(d/f)); 
System.out.println("(d-f)*(d-f)= " + new BigDecimal((d - f) * (d - f))); 

プリント

d= 0.299999999999999988897769753748434595763683319091796875 
f= 0.1000000000000000055511151231257827021181583404541015625 
d+f= 0.40000000000000002220446049250313080847263336181640625 
0.4= 0.40000000000000002220446049250313080847263336181640625 
d*f= 0.0299999999999999988897769753748434595763683319091796875 
0.03= 0.0299999999999999988897769753748434595763683319091796875 
d-f= 0.1999999999999999833466546306226518936455249786376953125 
0.2= 0.200000000000000011102230246251565404236316680908203125 
d/f= 2.999999999999999555910790149937383830547332763671875 
(d-f)*(d-f)= 0.03999999999999999389377336456163902767002582550048828125 

あなたは0.1が少し大きすぎると0.3が少し小さすぎることがわかります。これは、あなたがそれらを追加または掛け合わせると、ほぼ正しい数を得ることを意味します。しかし、減算または除算を使用すると、エラーが蓄積され、表現された数から離れた数が得られます。

つまり、0.1と0.3が0.4と同じ値になり、0.3 - 0.1が0と同じ値にならないことがわかります。2


ところであなたは

System.out.printf("d-f= %.2f%n", d - f); 
System.out.printf("d/f= %.2f%n", d/f); 
System.out.printf("(d-f)*(d-f)= %.2f%n", (d - f) * (d - f)); 

プリント

d-f= 0.20 
d/f= 3.00 
(d-f)*(d-f)= 0.04 

または

System.out.println("d-f= " + roundTo6Places(d - f)); 
System.out.println("d/f= " + roundTo6Places(d/f)); 
System.out.println("(d-f)*(d-f)= " + roundTo6Places((d - f) * (d - f))); 

public static double roundTo6Places(double d) { 
    return (long)(d * 1e6 + (d > 0 ? 0.5 : -0.5))/1e6; 
} 

プリント

を使用することができるのBigDecimalを使用せずに答えを丸めます
System.out.println("d-f= " + roundTo6Places(d - f)); 
System.out.println("d/f= " + roundTo6Places(d/f)); 
System.out.println("(d-f)*(d-f)= " + roundTo6Places((d - f) * (d - f))); 

丸めは0.1

double before_f = Double.longBitsToDouble(Double.doubleToLongBits(f) - 1); 
System.out.println("The value before 0.1 is " + new BigDecimal(before_f) + " error= " + BigDecimal.valueOf(0.1).subtract(new BigDecimal(before_f))); 
System.out.println("The value after 0.1 is " + new BigDecimal(f) + " error= " + new BigDecimal(f).subtract(BigDecimal.valueOf(0.1))); 
として計算することができる前と後に表すことができる値丸め誤差(のtoStringを処理するように設計されている唯一のエラーを残す)


を除去します

プリント

The value before 0.1 is 0.09999999999999999167332731531132594682276248931884765625 
    error= 8.32667268468867405317723751068115234375E-18 
The value after 0.1 is 0.1000000000000000055511151231257827021181583404541015625 
    error= 5.5511151231257827021181583404541015625E-18 
+0

ありがとう:)なぜその0.1はその実績より多く、0.3少ないですか? – Ahamed

+0

これは最も近い表現可能な値であるため、 '0.1'はやや多くなります。 「0.3」の場合、最も近い表現可能な値はわずかに少ない。 –

+0

OMG!それはなぜですか:(最も近いものは実際にはどういう意味ですか? – Ahamed

5

あなたがしている場合精度の高い使用には絶望的BigDecimal。

public static void main(String[] args) { 
    BigDecimal d = BigDecimal.valueOf(0.3d); 
    BigDecimal f = BigDecimal.valueOf(0.1d); 
    System.out.println(d.add(f)); 
    System.out.println(d.multiply(f)); 
    System.out.println(d); 
    System.out.println(f); 
    System.out.println(d.subtract(f)); 
    System.out.println(d.divide(f)); 
    System.out.println((d.subtract(f)).multiply(d.subtract(f))); 
} 

出力

0.4 
0.03 
0.3 
0.1 
0.2 
3 
0.04 

またはあなたの結果ラウンド、のDecimalFormatにのみ必要

double d = 0.3d; 
    double f = 0.1d; 
    DecimalFormat format = new DecimalFormat("#.##"); 
    System.out.println(format.format(d + f)); 
    System.out.println(format.format(d * f)); 
    System.out.println(format.format(d)); 
    System.out.println(format.format(f)); 
    System.out.println(format.format(d - f)); 
    System.out.println(format.format(d/f)); 
    System.out.println(format.format((d - f) * (d - f))); 

出力

0.4 
0.03 
0.3 
0.1 
0.2 
3 
0.04 
関連する問題