web-dev-qa-db-ja.com

丸め誤差?

私のコースでは、次のように言われています。

連続値はおおよそメモリ内で表されるため、浮動小数点数を使用した計算には丸め誤差が伴います。これらはビットパターンの小さな不一致です。したがって、efがfloatの場合、テストe==fは安全ではありません。

Javaを指します。

これは本当ですか? doublesおよびfloatsとの比較ステートメントを使用しましたが、丸めの問題は発生していません。似たような教科書を読んだことはありません。確かに、仮想マシンがこれを説明していますか?

23
Humphrey Bogart

それは本当です。

これは、浮動小数点値がメモリ内で有限ビット数で表される方法に固有の制限です。

たとえば、このプログラムは「false」を出力します。

public class Main {
  public static void main(String[] args) {
    double a = 0.7;
    double b = 0.9;
    double x = a + 0.1;
    double y = b - 0.1;
    System.out.println(x == y);
  }
}

'=='と正確に比較する代わりに、通常、ある程度の精度を決定し、数値が「十分に近い」かどうかを尋ねます。

System.out.println(Math.abs(x - y) < 0.0001);
52
Chris Vest

これは、浮動小数点を使用する他の言語と同じようにJavaに適用されます。これは、ハードウェアでの浮動小数点値の表現の設計に固有のものです。

浮動小数点値の詳細:

すべてのコンピューター科学者が浮動小数点演算について知っておくべきこと

26
Ben Schwehn

はい、0.1を基数2で正確に表すことは、1/3を基数10で正確に表現しようとすることと同じです。

8
duffymo

これは常に真実です。浮動小数点表現では正確に表現できない数値がいくつかあります。たとえば、円周率について考えてみます。有限のストレージ内で、無限の桁を持つ数をどのように表現しますか?したがって、数値を比較するときは、それらの差がイプシロンよりも小さいかどうかを確認する必要があります。また、BigDecimalやBigIntegerなど、より高い精度を実現するのに役立つクラスがいくつか存在します。

4
laginimaineb

そうです。 Javaはそれとは何の関係もないことに注意してください。問題は、[〜#〜] any [〜#〜]言語の浮動小数点演算に固有のものです。

多くの場合、教室レベルの問題でそれを回避することができますが、現実の世界では機能しません。教室でうまくいかないこともあります。

昔から学校に戻った事件。イントロクラスの教師は、最終試験の問題を割り当てました。これは、多くの優れた生徒にとって本当に厄介な問題でした。それは機能せず、理由もわかりませんでした。 (私はこれを実験助手として見ました。私はクラスにいませんでした。)最後に、助けを求め始めた人もいれば、問題を明らかにした人もいました。浮動小数点演算の本質的な不正確さについて教えられたことは一度もありませんでした。

さて、この問題には2つの基本的なアプローチがありました。ブルートフォース攻撃(この場合、毎回同じエラーが発生するため、偶然に機能しました)とよりエレガントなアプローチ(異なるエラーが発生して機能しません)です。エレガントなアプローチを試してみると、理由がわからずにレンガの壁にぶつかるでしょう。私は彼らの多くを助け、理由を説明するコメントを残し、彼に質問があれば私に連絡しました。

もちろん、次の学期は彼からこれについて聞いて、私は基本的に簡単な小さなプログラムで部門全体を床に置きました:

10 X = 3000000
20 X = X + 1
30 If X < X + 1 goto 20
40 Print "X = X + 1"

学科のすべての教師が考えたことにもかかわらず、これは[〜#〜] will [〜#〜]終了します。 300万のシードは、単にそれをより速く終了させるためのものです。 (基本がわからない場合:ここにはギミックはなく、浮動小数点数の精度を使い果たしているだけです。)

4
Loren Pechtel

はい、他の回答が言っているように。浮動小数点の精度に関するこの記事をお勧めすることを付け加えたいと思います: 浮動小数点の視覚化

2
Artur Soler

もちろんそれは本当です。考えてみてください。すべての数値は2進数で表す必要があります。

写真:「1000」は0.5または1/2、つまり2 **-1です。その場合、「0100」は0.25または1/4です。あなたは私がどこに行くのか見ることができます。

この方法でいくつの数字を表すことができますか? 2 ** 4。ビットを追加すると、使用可能なスペースが複製されますが、無限になることはありません。 1/3または1/10、問題1/nについては、2の倍数でない任意の数を実際に表すことはできません。

1/3は、「0101」(0.3125)または「0110」(0.375)の場合があります。 3を掛けると、どちらの値も1にはなりません。もちろん、特別なルールを追加することもできます。 「「0101」を3回追加したら、1にします」と言います...このアプローチは、長期的には機能しません。捕まえることはできますが、1/6×2はどうですか?

これは2進表現の問題ではありません。有限表現には表現できない数があり、結局のところ無限です。

2
eipipuz

ほとんどのCPU(およびコンピューター言語)は、IEEE754浮動小数点演算を使用します。この表記法を使用すると、この表記法で正確に表現されていない10進数があります。 0.1。したがって、1を10で割ると、正確な結果は得られません。連続して複数の計算を実行すると、エラーが合計されます。 Pythonで次の例を試してください。

>>> 0.1
0.10000000000000001
>>> 0.1 / 7 * 10 * 7 == 1
False

それはあなたが数学的に期待するものではありません。

ちなみに、浮動小数点数に関する一般的な誤解は、結果が正確ではなく、安全に比較できないというものです。これは、実際に数の端数を使用する場合にのみ当てはまります。すべての計算が整数領域にある場合、doubleとfloatはintとまったく同じように機能し、安全に比較することもできます。たとえば、ループカウンターとして安全に使用できます。

2
Nikolai Ruhe

はい、Javaも 浮動小数点 算術を使用します。

1
dfa