面接官:BigDecimalは精度を失わないでしょうか.

6127 ワード

小Hubリーダー:
Doubleが精度を失うことを知っていますが、BigDecimalはできませんか?正しく使ってほしい!
作者:ヒカリCP
出典:www.jianshu.com/p/c 81 edc 59546 c
私たちは基本的に常識を形成しており、お金を使う必要がある場所はBigDecimalで他のものではなく、浮動小数点型変数が計算を行う際に精度を失う問題があることも知っています.
では、実はBigDecimalも精度を失うことを知っていますか?BigDecimalの背後には何か探究すべきところがありますか?今日、あなたに教えて、その然を知っていて、その所以然も知っています.
次のコードがあります.
System.out.println(0.05 + 0.01);  
System.out.println(1.0 - 0.42);  
System.out.println(4.015 * 100);  
System.out.println(123.3 / 100);  

出力:0.060000000000000000050.58000000000001401.499999999999941.23999999999999999999999
Javaで浮動小数点数演算を行うと精度が落ちるという問題が見られます.では、商品の価格計算を行うと、問題が発生します.
私たちの手には0.06元ある可能性が高いが、0.05元と0.01元の商品を買うことができない.
以上のように、2つの合計は0.06000000000000です.
これは間違いなく深刻な問題であり、特に電子商取引サイトの合併量が上昇すると、発生する問題は巨大になるだろう.注文ができなくなったり、帳簿に問題が発生したりする可能性があります.次に、JavaのBigDecimalクラスを使用して、このような問題を解決することができます.
普及してみましょう.
Javaにおけるfloatの精度は6-7ビット有効数字である.doubleの精度は15-16ビットです.
API
コンストラクタ:
                        
BigDecimal(int)                        。
BigDecimal(double)                      。
BigDecimal(long)                        。
BigDecimal(String)                           。

関数:
                        
add(BigDecimal)       BigDecimal       ,        。
subtract(BigDecimal)  BigDecimal       ,        。
multiply(BigDecimal)  BigDecimal       ,        。
divide(BigDecimal)    BigDecimal       ,        。
toString()             BigDecimal           。
doubleValue()          BigDecimal            。
floatValue()           BigDecimal            。
longValue()            BigDecimal           。
intValue()             BigDecimal          。

一般的な数値タイプのため、例えばdoubleは16ビット以上の数字を正確に表すことができない.
BigDecimalでも精度が失われています
BigDecimalを使用する場合、そのBigDecimal(String)コンストラクタを使用してオブジェクトを作成することは意味があります.他にもBigDecimal b=new BigDecimal(1)のように精度が落ちるという問題が発生する.次のコードがあります.
BigDecimal a = new BigDecimal(1.01);
BigDecimal b = new BigDecimal(1.02);
BigDecimal c = new BigDecimal("1.01");
BigDecimal d = new BigDecimal("1.02");
System.out.println(a.add(b));
System.out.println(c.add(d));

出力:2.0300000000026645259100375607016716003417968752.03
可視論的損失精度BigDecimalはより過剰であった.しかし,Bigdecimalを用いたBigDecimal(String)コンストラクタの変数は,演算を行う際にはこのような問題は生じなかった.
その原因を究明すると、コンピュータの構成原理には、それらの符号化がこのような結果を決定している.
longは19ビットの数字を正確に格納できますが、doubleは16ビットの数字しか格納できません.
doubleはexpビットがあるため、16ビット以上の数字を格納することができるが、低位の不正確さを代価とする必要がある.19桁以上の正確なストレージが必要な場合は、BigIntegerで保存する必要があります.もちろん、パフォーマンスの犠牲になります.
そこで,一般的にBigDecimalを用いて商業演算上の精度の喪失の問題を解決する際には,BigDecimalオブジェクトを宣言する際には必ずStringのパラメータを持つタイプのコンストラクタを構築する.
また、この原則はEffective JavaやMySQLの必知必会にも言及されている.floatとdoubleは科学計算とエンジニアリング計算にしか使えない.ビジネス演算ではBigDecimalを使用します.
また、BigDecimalクラスのdoubleタイプパラメータのコンストラクタの注釈の一部を以下に示すように、ソースコードの注釈から公式に説明します.
* The results of this constructor can be somewhat unpredictable.  
     * One might assume that writing {@codenew BigDecimal(0.1)} in  
     * Java creates a {@code BigDecimal} which is exactly equal to  
     * 0.1 (an unscaled value of 1, with a scale of 1), but it is  
     * actually equal to  
     * 0.1000000000000000055511151231257827021181583404541015625.  
     * This is because 0.1 cannot be represented exactly as a  
     * {@codedouble} (or, for that matter, as a binary fraction of  
     * any finite length).  Thus, the value that is being passed  
     * in to the constructor is not exactly equal to 0.1,  
     * appearances notwithstanding.  
       ……  
        * When a {@codedouble} must be used as a source for a  
     * {@code BigDecimal}, note that this constructor provides an  
     * exact conversion; it does not give the same result as  
     * converting the {@codedouble} to a {@code String} using the  
     * {@link Double#toString(double)} method and then using the  
     * {@link #BigDecimal(String)} constructor.  To get that result,  
     * use the {@codestatic} {@link #valueOf(double)} method.  
     *   
public BigDecimal(double val) {  
    this(val,MathContext.UNLIMITED);  
}  

第1段も計算できるのは無限にこの数に近いことをよく言っていますが、この数まで正確にはできません.
2番目のセグメントでは、この値を正確に計算するにはdoubleタイプのパラメータをStringタイプに変換する必要があります.さらにBigDecimal(String)という構造手法を用いて構築を行った.結果を取得します.
BigDecimalを正しく運用
また、BigDecimalが作成したオブジェクトは、従来の+、-、*、/などの算術演算子を使用して直接オブジェクトを数学的に演算するのではなく、対応するメソッドを呼び出す必要があります.メソッドのパラメータもBigDecimalのオブジェクトでなければなりません.さっき羅列したAPIからもわかります.
一般的な開発では、私たちのデータベースに格納されているデータはfloatタイプとdoubleタイプです.持っていく演算を行うには、絶えず変換する必要があり、非常に不便です.ここではツールクラスを書きました.
/**  
 * @author: Ji YongGuang.  
 * @date: 19:50 2017/12/14.  
 */  
publicclass BigDecimalUtil {  

    private BigDecimalUtil() {  

    }  

    public static BigDecimal add(double v1, double v2) {// v1 + v2  
        BigDecimal b1 = new BigDecimal(Double.toString(v1));  
        BigDecimal b2 = new BigDecimal(Double.toString(v2));  
        return b1.add(b2);  
    }  

    public static BigDecimal sub(double v1, double v2) {  
        BigDecimal b1 = new BigDecimal(Double.toString(v1));  
        BigDecimal b2 = new BigDecimal(Double.toString(v2));  
        return b1.subtract(b2);  
    }  

    public static BigDecimal mul(double v1, double v2) {  
        BigDecimal b1 = new BigDecimal(Double.toString(v1));  
        BigDecimal b2 = new BigDecimal(Double.toString(v2));  
        return b1.multiply(b2);  
    }  

    public static BigDecimal div(double v1, double v2) {  
        BigDecimal b1 = new BigDecimal(Double.toString(v1));  
        BigDecimal b2 = new BigDecimal(Double.toString(v2));  
        // 2 =            ROUND_HALF_UP =       
        return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP);//           
    }  
}  

このツールクラスはdoubleタイプの基本的な加算減算演算を提供します.直接呼び出せばいいです.
(完)
MarkerHub記事インデックス:(原文を読む直通をクリック)
https://github.com/MarkerHub/JavaIndex
【    】
          

    API  ,   ,    ,   ?
    !java retry(  ) spring retry, guava retrying   
     Spring Boot        starter
          ?
               ,      !


いい文章だ!ちょっと見てる!