BigDecimal簡介
Java在java.math包中提供的API類BigDecimal粉私,用來對超過16位有效位的數進行精確的運算。雙精度浮點型變量double可以處理16位有效數憎瘸。在實際應用中,需要對更大或者更小的數進行運算和處理锅风。float和double只能用來做科學計算或者是工程計算咖驮,在商業(yè)計算中要用java.math.BigDecimal。BigDecimal所創(chuàng)建的是對象,我們不能使用傳統(tǒng)的+十酣、-也颤、*、/等算術運算符直接對其對象進行數學運算燥翅,而必須調用其相對應的方法谎势。方法中的參數也必須是BigDecimal的對象猖毫。構造器是類的特殊方法坞生,專門用來創(chuàng)建對象又兵,特別是帶有參數的對象。
問題分析
今天遇到一個bug俄烁,用戶輸入一個金額1219.4页屠,然后客戶端將金額上傳到服務器牢贸,這里客戶端是通過構造一個BigDecimal對象潜索,使用的構造方法如下
BigDecimal(double val)
平時都沒問題的整陌,可是今天報了bug,調試一看發(fā)現轉換后的值為1219.40000000000009094947017729282379150390625澜搅,超出了服務器要求的長度,很明顯精度有問題邪锌。
至于為什么為這樣呢,看一下源碼(其實只看了注釋蜕企,源碼看不懂)
/**
* Constructs a new {@code BigDecimal} instance from the 64bit double
* {@code val}. The constructed big decimal is equivalent to the given
* double. For example, {@code new BigDecimal(0.1)} is equal to {@code
* 0.1000000000000000055511151231257827021181583404541015625}. This happens
* as {@code 0.1} cannot be represented exactly in binary.
* <p>
* To generate a big decimal instance which is equivalent to {@code 0.1} use
* the {@code BigDecimal(String)} constructor.
*
* @param val
* double value to be converted to a {@code BigDecimal} instance.
* @throws NumberFormatException
* if {@code val} is infinity or not a number.
*/
public BigDecimal(double val) {
//這里源碼省略...
}
注釋的大概意思就是說有時候你看到的值轻掩,不一定是真實的值丐重,如0.1崖蜜,它的真實值是0.1000000000000000055511151231257827021181583404541015625這個東西氧卧,因為0.1無法用二進制表示,double是預估的值,不是準確值
如何解決
BigDecimal的用法有以下三種:
//不推薦使用撤逢,因為它不能準確的給出相應的值
BigDecimal(double val)
//推薦使用,因為String是可預期的,new BigDecimal("0.1")的值還是0.1
BigDecimal(String val)
//如果你非要用double蚊荣,也可以使用以下這個方法初狰,它一個靜態(tài)的方法,但其實還是把double轉成了String
valueOf(double val)
//valueOf源碼如下
public static BigDecimal valueOf(double val) {
if (Double.isInfinite(val) || Double.isNaN(val)) {
throw new NumberFormatException("Infinity or NaN: " + val);
}
return new BigDecimal(Double.toString(val));
}
所以解決辦法就是互例,盡量的去使用String的構造方法BigDecimal(String val)奢入,非要用double呢,就使用BigDecimal.valueOf(double val)
結束語
下面這個是我調試的截圖媳叨,有興趣的朋友可以自行去試試俊马,平時還沒注意,今天發(fā)現這個問題肩杈,1219.4和1219.5,一個不可以一個可以解寝,感覺還是蠻有趣的扩然,記錄一下,又學到一個知識點聋伦,做個筆記夫偶。