Javaにおけるdoubleとfloatの精度損失の問題と解決方法


原文住所:http://www.cnblogs.com/cblogs/p/double-precision.html
2つのdouble数0.1と0.2の加算を議論すると,それらの加算の結果は0.3であることは間違いない.しかし、問題はいつもそうですか.
次の2つのdoubles数を加算し、出力結果を見てみましょう.
    @Test
    public void testBig(){
        
        System.out.println(0.11+2001299.32);
    }
    

コンソール出力2001299.4300000002
私たちが驚いたことに、結果は私たちが予想したほどではありません.これはなぜですか.どうやって解決しますか?
 
BigDecimalのコンストラクション関数のドキュメントを貼り付けて参考にします.
BigDecimal
public BigDecimal(double val)

doubleをBigDecimalに変換します.後者はdoubleのバイナリ浮動小数点値の正確な10進数表現です.返されるBigDecimalのスケールは(10 scale× val)は整数の最小値です.
注意:
この構造方法の結果には一定の予知性がある.Javaにnew BigDecimal(0.1)を書き込んで作成されたBigDecimalは、ちょうど0.1(スケール値1ではなく、スケール値1)に等しいと考えられているかもしれませんが、実際には0.100000000000 055123125782702181583404541015625に等しくなります.これは、0.1をdoubleと正確に表すことができないためである(または、この場合、任意の限られた長さのバイナリ小数として表すことができない).これにより、構造方法に入力される値は、表面的には0.1に等しくない.
一方、String構造方法は、new BigDecimal(「0.1」)に書き込むと、予想される0.1に等しいBigDecimalが作成されることが完全に予知されている.したがって、比較的には、通常、String構造方法を優先的に使用することが推奨される.
doubleがBigDecimalのソースとして使用する必要がある場合、この構造方法は正確な変換を提供することに注意してください.Double.toString(double)メソッドを先に使用し、BigDecimal(String)コンストラクションメソッドを使用してdoubleをStringに変換するという操作と同じ結果は提供されません.この結果を取得するには、static valueOf(double)メソッドを使用します.
 
解決方法:
上のドキュメントからも解決策が見つかったと信じています.
2桁の小数を正確に表す必要がある場合は、BigDecimalオブジェクトに変換してから演算する必要があります.
また注意が必要です
BigDecimal(double val)コンストラクション関数を使用しても精度が失われる問題があります.BigDecimal(String val)を使用することをお勧めします.
これは、まずdoubleを文字列に変換し、BigDecimal(String val)構造関数のパラメータとして使用する必要があります.BigDecimalオブジェクトに変換
その後、加減乗除操作を行い、精度に問題が発生しません.これも、お金に関するデータストレージがBigDecimalを使用している理由です.
    @Test
    public void testBig(){
        System.out.println(0.11+2001299.32);//      
        BigDecimal bigDecimal1 = new BigDecimal(Double.toString(0.11));
        BigDecimal bigDecimal2 = new BigDecimal(Double.toString(2001299.32));
        System.out.println(bigDecimal1.add(bigDecimal2));//     
    }


コンソール出力:
2001299.43000000022001299.43