0

私が知っているように、倍数はメモリに64ビットを予約します。浮動小数点数は 32 ビットを予約します。

私は次のことを試しました:

double result=0.1f+0.3f;
System.out.println(result);

その結果、印刷​​され0.4000000059604645ます。次のメモリ ブロックからのデータですか (最初の 32 ビットと次の 32 ビット)?
それは何か他のものですか?

4

1 に答える 1

8

この行:

double result=0.1f+0.3f;

最初に文字列 "0.1" と "0.3" を値に変換し、次にの規則を使用しfloatて 2 つの値を加算し、最後に結果を a に変換して代入を行います。float リテラルを値に変換すると、丸め誤差が発生します。(ちょうど 1/3 が基数 10 で無限に繰り返される展開を持つように、1/10 と 3/10 は基数 2 で無限に繰り返される展開を持ち、どちらでも正確に表すことはできません。(しゃれた意図)。からへの拡大拡張は正確ですが、システムは拡張された解像度に意味のあるデータが含まれていると想定します。floatfloatdoublefloatfloatdoublefloatfloatdouble

の規則を使用して加算を実行する場合は、キャストまたはリテラルを使用してdouble、少なくとも 1 つのオペランドを最初に変換する必要があります。doubledouble

double result=0.1f+0.3d;

上記では、0.3 はdouble完全なdouble精度に変換されます。0.1 は に変換されてからfloatに拡張されるためdoubledoubleよりも丸め誤差が大きい値になり0.1dます。ほとんどのコンパイラは、コンパイル時にこれを行います。

このコードを Oracle の javac コンパイラ (バージョン 1.7.0_11、デフォルト設定) でコンパイルします。

public class Foo1 {
    public static void main(String[] args) {
        double dff = 0.1f + 0.3f;
        double dfd = 0.1f + 0.3d;
        double ddd = 0.1d + 0.3d;
        System.out.println("dff=" + dff);
        System.out.println("dfd=" + dfd);
        System.out.println("ddd=" + ddd);
        System.out.println("dff==dfd: " + (dff == dfd));
        System.out.println("dff==ddd: " + (dff == ddd));
        System.out.println("dfd==ddd: " + (dfd == ddd));
        System.out.println("ddd==0.4: " + (10*ddd == 4));
    }
}

次のように始まるバイトコードを生成します (javap -c を使用して逆アセンブルします):

ldc2_w        #2                  // double 0.4000000059604645d
dstore_1
ldc2_w        #4                  // double 0.4000000014901161d
dstore_3
ldc2_w        #6                  // double 0.4d
dstore        5

次の出力が生成されます。

dff=0.4000000059604645  
dfd=0.4000000014901161  
ddd=0.4  
dff==dfd: false  
dff==ddd: false  
dfd==ddd: false  
ddd==0.4: true

0.1d と 0.3d の表現誤差と加算の丸め誤差 (存在する場合) が、0.4d の表現誤差と正確に一致する方法で組み合わされるため、最後の行は "true" を示しています。に変更した場合(および に変更0.3した場合)はあまり良くありません。0.210*ddd==410*ddd==3

一般に、floatまたはを使用するかどうかに関係なく、double丸め誤差が発生し、結果が正確になる可能性は低くなります。精度を拡張するには、 を使用する必要がありますBigDecimal。見栄えの良い出力が必要な場合は、書式指定子を使用します (たとえば、System.out.printf()またはを使用String.format())。

What Every Computer Scientist Should Know About Floating-Point Arithmeticもお読みください。

于 2013-07-12T15:50:15.777 に答える