3

浮動小数点数の精度に関する別の質問から生じた質問があります。

現在、浮動小数点は常に正確に表現できるとは限らないため、表現可能な最も近い浮動小数点数として格納されることがわかっています。

私の質問は、実際には と の表現の違いについてfloatですdouble

この質問はどこから生じますか?

私がそうするとします:

System.out.println(.475d+.075d);

その後、出力は(私のマシンでは)そう0.55ではありません0.549999

しかし、私がするとき:

System.out.println(.475f+.075f);

私は正しい答えを得る、つまり0.55(私にとっては少し予想外でした)

これまで、私はdoubleより精度の高い印象を受けていました ( double は小数点以下の桁数が長くなるまでより正確になります) float。したがって、double を正確に表現できない場合、同等の float 表現も不正確に格納されます。

しかし、私が得た結果は私にとって少し気がかりです。次の場合は混乱します。

  1. precision私は何を意味するかについて間違った理解をしていますか?
  2. floatdouble がより多くのビットを持ってdoubleいるという事実を除けば、表現方法は異なりますか?
4

3 に答える 3

8

として表現できる数は、floatとしても表現できdoubleます。

あなたが読んでいるのはフォーマットされた出力であり、実際のバイナリ表現は読んでいません。

System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(.475d + .075d)));
// 11111111100001100110011001100110011001100110011001100110011001
System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(.475f + .075f)));
// 111111000011001100110011001101

double d = .475d + .075d;
System.out.println(d);
// 0.5499999999999999
System.out.println((float)d);
// 0.55 (as expected)
System.out.println((double)(float)d);
// 0.550000011920929

System.out.println( .475f + .075f == 0.550000011920929d);
// true
于 2012-06-08T22:32:57.017 に答える
5

精度とは、より多くのビットを意味します。として表すことができない数は、float として正確に表すことができますdoubleが、それらのケースの数は、可能なケースの総数に比べて無限に小さいです。

のような単純なケース0.1では、使用可能なビット数に関係なく、固定長の浮動小数点数として表現できません。これは、使用できる桁数に関係なく (桁数が有限である限り)、1/7 などの分数を 10 進数で正確に表すことができないと言っているのと同じです。0.142857142857142857... のように近似することはできますが、何度も繰り返しても正確に書くことはできません。

逆に、数値が として正確に表現できる場合、数値floatも として正確に表現できdoubleます。double は、より大きな指数範囲とより多くの仮数ビットを持ちます。

あなたの例では、明らかな不一致の原因は、floatでは、0.475 とその float 表現の差が「正しい」方向にあったため、切り捨てが発生したときに期待どおりになったことです。利用可能な精度を上げると、表現は 0.475 に「近づき」ましたが、現在は反対側にあります。大まかな例として、最も近い可能性のある float が 0.475006 で、double では最も近い可能性のある値が 0.474999 であるとしましょう。これにより、表示される結果が得られます。

編集:簡単な実験の結果は次のとおりです。

public class Test {

    public static void main(String[] args)
    {
        float  f = 0.475f;
        double d = 0.475d;

        System.out.printf("%20.16f", f);
        System.out.printf("%20.16f", d);
    }
}

出力:

  0.4749999940395355  0.4750000000000000

これが意味することは、数値 0.475 の浮動小数点表現は、ビット数が膨大な場合、0.475 よりわずかに小さいということです。これは二重表現で見られます。ただし、最初の「間違った」ビットはかなり右側にあるため、a に収まるように切り捨てられると、floatたまたま 0.475 になります。これは完全に事故です。

于 2012-06-08T22:23:56.977 に答える
1

浮動小数点型が離散値ではなく実際に値の範囲を0.1f表していると考える場合 (たとえば、13421773/134217728 ではなく、「13421772.5/134217728 と 13421773.5/134217728 の間の何か」)、 からdoubleへの変換floatは通常正確です。 、一方 からfloatへの変換doubleは通常そうではありません。残念なことに、Java では、通常は正確な方向への型キャストを要求しながら、通常は不正確な変換を暗黙的に実行できます。

type のすべての値に対して、 の範囲の中心を中心とする範囲floatの type の値が存在します。これは、 が float の値を正確に表しているという意味ではありません。たとえば、 に変換すると、「13421772.9999999/134217728 と 13421773.0000001/134217728 の間の値」を意味する値が得られます。この値は、暗黙の許容範囲の 100 万倍以上ずれています。doublefloatdouble0.1fdouble

type のほとんどすべての値に対して、 によって暗示された範囲を完全に含む範囲doubleの type の値が存在します。唯一の例外は、範囲が 2 つの値の境界を正確に中心とする値です。このような値を に変換するには、システムがいずれかの範囲を選択する必要があります。がその範囲の中心より下の数値を実際に表しているときにシステムが切り上げを行う場合、またはその逆の場合、 の範囲は の範囲を完全には包含しません。ただし、実際には、これは問題ではありません。floatdoublefloatfloatdoublefloatdoublefloatdouble(13421772.5/134217728 から 13421773.5/134217728) のような範囲を表す場合、(13421772.4999999/134217728 から 13421773.5000001/134217728) のような範囲を表します。floattodoubleキャストから生じる恐ろしい不正確さと比較すると、その小さな不正確さは何でもありません。

ところで、使用している特定の数値に戻ると、計算を float として行う場合、計算は次のようになります。

0.075f = 20132660±½ / 268435456
0.475f = 31876710±½ / 67108864
合計 = 18454938±½ / 33554432

つまり、合計は、およそ 0.54999999701 から 0.55000002682 の間の数値を表します。最も自然な表現は 0.55 です (実際の値はそれよりも大きくなったり小さくなったりする可能性があるため、数字を追加しても意味がありません)。

于 2012-06-08T22:53:56.213 に答える