[解決済み] なぜ0.1を何度も足すとロスレスのままなのか?
質問
は知っています。
0.1
10進数は有限の2進数では正確に表現できない(
説明
) のため
double n = 0.1
は精度を失い、正確には
0.1
. 一方
0.5
であるため、正確に表現することができます。
0.5 = 1/2 = 0.1b
.
というのは理解できるのですが
0.1
3回
は、正確に与えることはできません。
0.3
というわけで、次のコードは次のように表示されます。
false
:
double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
sum += d;
System.out.println(sum == 0.3); // Prints false, OK
しかし、それならどうして
0.1
5回
は、まさに
0.5
? 次のコードは、次のように表示します。
true
:
double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?
もし
0.1
は正確に表現できないのに、どうして5回足すと正確に
0.5
というのは、正確に表すことができるのでしょうか?
どのように解決するのですか?
丸め誤差はランダムではなく、その実装方法は誤差を最小にしようとします。 つまり、誤差が見えない、あるいは誤差がない場合があります。
例えば
0.1
は正確には
0.1
すなわち
new BigDecimal("0.1") < new BigDecimal(0.1)
しかし
0.5
はまさに
1.0/2
このプログラムでは、関係する真の値を表示します。
BigDecimal _0_1 = new BigDecimal(0.1);
BigDecimal x = _0_1;
for(int i = 1; i <= 10; i ++) {
System.out.println(i+" x 0.1 is "+x+", as double "+x.doubleValue());
x = x.add(_0_1);
}
印刷物
0.1000000000000000055511151231257827021181583404541015625, as double 0.1
0.2000000000000000111022302462515654042363166809082031250, as double 0.2
0.3000000000000000166533453693773481063544750213623046875, as double 0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500, as double 0.4
0.5000000000000000277555756156289135105907917022705078125, as double 0.5
0.6000000000000000333066907387546962127089500427246093750, as double 0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375, as double 0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000, as double 0.8
0.9000000000000000499600361081320443190634250640869140625, as double 0.9
1.0000000000000000555111512312578270211815834045410156250, as double 1.0
注:その
0.3
が少しずれていますが、その時に
0.4
の場合、53ビットの制限に合わせるためにビットを1つ下げなければならず、エラーは破棄されます。再び、エラーが
0.6
と
0.7
しかし
0.8
から
1.0
の場合、エラーは破棄されます。
5回足すとエラーがキャンセルされるのではなく、累積されるはずです。
エラーが発生する理由は、精度が53ビットと限られているためです。 つまり、数値が大きくなるにつれて、より多くのビットが使われるようになり、末尾のビットが削除されなければならなくなるのです。このため、丸め誤差が生じますが、この場合はあなたに有利に働きます。
小さい数値を取得する場合は、逆の効果が得られます。
0.1-0.0999
=>
1.0000000000000286E-4
と表示され、以前より多くのエラーが表示されます。
この例として、Java6ではなぜか なぜ Math.round(0.4999999999994) は 1 を返すのでしょうか? この場合、計算におけるビットの損失は、結果として答えに大きな違いをもたらします。
関連
-
eclipse アクセス制限です。タイプ 'xxx' は API ではありません(必須ライブラリ '' の制限)。
-
JavaMailのメール送信が失敗するケースとその説明の分析
-
JQuery DataTable 详解
-
Spring boot runs with Error creating bean with name 'entityManagerFactory' defined in class path resource
-
あるコードに出会いましたが、何に使うのか理解できません。 List<String> list = new ArrayList<String>() { { a
-
[解決済み] この2回(1927年)を引き算すると、なぜおかしな結果になるのでしょうか?
-
[解決済み] ランダムな文字列を使用するこのコードは、なぜ "hello world" と表示されるのですか?
-
[解決済み] 0.1fを0にすると、なぜ10倍もパフォーマンスが落ちるのですか?
-
[解決済み] なぜJavaにはtransientフィールドがあるのですか?
-
[解決済み] Math.round(0.4999999999994) はなぜ1を返すのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Java のエラーです。未解決のコンパイル問題 解決方法
-
undefinedeclipse エラー。この行に複数のアノテーションが見つかりました: - 文字列を型解決に解決できない
-
javaの実行中に「javaの例外が発生しました」と表示された場合はどうすればよいですか?
-
eclipse アクセス制限です。タイプ 'xxx' は API ではありません(必須ライブラリ '' の制限)。
-
Javaクラスローダーにソースコードから潜り込む
-
エラーの解決方法 jarfile XXX.jarにアクセスできません。
-
Javaエラーメッセージがenclosingクラスでない
-
[オリジナル】java学習ノート【II】よくあるエラー クラスパス上のクラスファイルが見つからない、またはアクセスできない場合
-
SocketTimeoutExceptionです。読み込みがタイムアウトしました
-
[解決済み] Math.round(0.4999999999994) はなぜ1を返すのですか?