4

私はJavaで簡単な部門を持っています:

float f = 19.7f/100;
System.out.println(f); // 0.19700001

double d = 19.7/100;
System.out.println(d); // 0.19699999999999998

なぜこれが起こるのですか?

4

4 に答える 4

2

これは、これまでで最もよく聞かれる質問の 1 つです。

  1. コンピューターは有限の桁数しか表現できないため、数値を格納して後で割るときに四捨五入を行う必要があります。この丸めは自然にエラーを生成しますが、たとえば 3 桁の精度だけが必要な場合は、問題にならないはずです。

  2. コンピュータは数値を 2 進数で格納するため、丸めの動作は少し予測できません。したがって、19.7 は 10 進数で終了しますが、同じ数値は 2 進数で繰り返す 10 進数です。つまり、10011.10110011001100110011001100110011... ... したがって、任意の点で四捨五入すると、10 進数の終了式から予測できない動作が生じることがわかります。

于 2013-03-02T07:07:41.073 に答える
0

除算のせいではなく、問題は精度の低下による 1.7f != 1.7 です。値のビット表現を見ることができます

    float f = 19.7f; 
    double d = 19.7;
    System.out.println(Double.doubleToLongBits(f)); 
    System.out.println(Double.doubleToLongBits(d));

出力

4626238274938077184
4626238274723328819
于 2013-03-02T07:14:40.247 に答える
0

Java は IEEE754 浮動小数点数を使用して float と double を処理します。この標準は、基数 10 を正確に表すために使用できない基数 2 の数値を使用するように設計されています。ここhttp://en.wikipedia.org/wiki/IEEE_floating_pointを参照してください。

以下は厳密には標準ではありませんが、基数 2 の浮動小数点が他の基数に適していない理由を理解するための例です。

base2 = base10
 0001 = 0001 -> 0*8 + 0*4 + 0*2 + 1*1 から
 0010 = 0002 -> 0*8 + 0*4 + 1*2 + 0*1 から
 0011 = 0003 -> 0*8 + 0*4 + 1*2 + 1*1 から
 0100 = 0004 -> 0*8 + 1*4 + 0*2 + 0*1 から
 0101 = 0005 -> 0*8 + 1*4 + 0*2 + 1*1 から
                   8 = 2^3、4 = 2^2、2 = 2^1、1 = 2^0

それで
base2 = base10
 .0000 = .0000 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .0001 = .0625 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .0010 = .1250 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .0011 = .1875 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .0100 = .2500 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .0101 = .3125 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .0110 = .3750 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .0111 = .4375 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1000 = .5000 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1001 = .5625 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1010 = .6250 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1011 = .6875 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1100 = .7500 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1101 = .8125 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1110 = .8700 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から
 .1111 = .9325 -> 0*1 + 0.5*0 + 0.25*0 + 0.125*0 + 0.0625*0 から

                   1 = 2^0、0.5 = 2^-1、0.25=2^-2、0.125 = 2^-3

ご覧のように。4 ビット浮動小数点数は、0 から 0.9325 までの 10 進数のみを表すことができ、ギャップは 0.0625 です。そして、それはまた、0.1、0.2、0.3 ... を実行できないことを意味します。

実際の標準では、桁シフト技術を使用するだけでなく、より多くのビットを使用しています。実際にはこの例よりも多くの数を表すことができますが、制限は同じです。したがって、ある値を割った結果がこれらのいずれにも当てはまらない場合、JVM はそれを最も近い値に移動します。

これが説明することを願っています。

于 2013-03-02T07:19:38.057 に答える
0

Mystical が提供したリンクは必読ですが、少し分厚いです。より初心者向けのバージョンについては、このサイトを試してください。

tl;dr は、浮動小数点演算は常に丸めの対象であり、double は精度が高いため、float とは異なる方法で丸められるということです。55 を 10 の位に丸めると 60 になり、100 の位に丸めると 100 になるのと少し似ています。

この場合、10 進数の 0.197 (さらに言えば 19.7) を float または double で正確に表現することはできないため、それぞれがその値に最も近い、表現できる数値を示します。double の方が精度が高いため、もう少し近づけることができます。

于 2013-03-02T07:02:42.737 に答える