Java中double轉BigDecimal的注意事項


先上結論:不要直接用double變量作為構造BigDecimal的參數。

 

線上有這么一段Java代碼邏輯:

1,接口傳來一個JSON串,里面有個數字:57.3。

2,解析JSON並把這個數字保存在一個float變量。

3,把這個float變量賦值給一個 BigDecimal對象,用的是BigDecimal的double參數的構造:

   new BigDecimal(double val)

4,把這個BigDecimal保存到MySQL數據庫,字段類型是decimal(15,2)。

 

這段代碼邏輯在線上跑了好久了,數據庫保存的值是57.3也沒什么問題,但是在今天debug的時候發現,第三步的BigDecimal對象保存的值並不是57.3,而是57.299999237060546875,很明顯,出現了精度的問題。

至於數據庫最終保存了正確的57.3完全是因為字段類型設置為2位小數,超過2位小數就四舍五入,所以才得到了正確的結果,相當於MySQL給我們把這個精度問題掩蓋了。

 

總覺得這是個坑,所以研究了一下相關的知識。

首先是BigDecimal的double參數構造,在官方JDK文檔中對這個構造是這么描述的:

public BigDecimal(double val)

Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.

Notes:

The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a 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 double (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.

The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.

When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.

Parameters:

val - double value to be converted to BigDecimal.

Throws:

NumberFormatException - if val is infinite or NaN.

翻譯一下大概是這樣的:

1,BigDecimal(double val)構造,用double當參數來構造一個BigDecimal對象。

2,但是這個構造不太靠譜(unpredictable),你可能以為BigDecimal(0.1)就是妥妥的等於0.1,但是你以為你以為的就是你以為的?還真不是,BigDecimal(0.1)這貨實際上等於0.1000000000000000055511151231257827021181583404541015625,因為准確的來說0.1本身不能算是一個double(其實0.1不能代表任何一個定長二進制分數)。

3,BigDecimal(String val)構造是靠譜的,BigDecimal(“0.1”)就是妥妥的等於0.1,推薦大家用這個構造。

4,如果你非得用一個double變量來構造一個BigDecimal,沒問題,我們貼心的提供了靜態方法valueOf(double),這個方法跟new Decimal(Double.toString(double))效果是一樣的。

說白了就是別直接拿double變量做參數,最好使用String類型做參數或者使用靜態方法valueOf(double),我寫了個例子試了一下:

  

       public static void main(String[] args) {



                   float a=57.3f;

                   BigDecimal decimalA=new BigDecimal(a);

                   System.out.println(decimalA);

                  

                   double b=57.3;

                   BigDecimal decimalB=new BigDecimal(b);

                   System.out.println(decimalB);

                  
                   //通常采用下面兩種模式 
                   double c=57.3;

                   BigDecimal decimalC=new BigDecimal(Double.toString(c));

                   System.out.println(decimalC);  

                  

                   double d=57.3;

                   BigDecimal decimalD=BigDecimal.valueOf(d);

                   System.out.println(decimalD);

         }
 

輸出結果:

57.299999237060546875

57.2999999999999971578290569595992565155029296875

57.3

57.3

以后還是盡量按照官方推薦的套路來,否則不知道什么時候又給自己挖坑了。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM