公司同事用 Float 和 Double ,結果導致..
BigDecimal 阿粉相信大家對這個肯定不陌生,只要你公司的業務中涉及到一些比較精確的數字的時候,都會使用 BigDecimal,而不會去使用 Float 和 double,并且在數據庫做設計的時候,如果是小數類型,也是會讓你使用 BigDecimal 而不是 float 和 double。為什么呢?阿粉來解釋一下。
float和double
float 單精度浮點數在機內占 4 個字節,用 32 位二進制描述
double 雙精度浮點數在機內占 8 個字節,用 64 位二進制描述
注意float型定義的數據末尾必須有"f"或"F",為了和double區別
我們來寫一段簡單的程序來實驗一下為什么它不行
- System.out.println(2.0-1.4);
如果是有經驗的開發人員,肯定覺得這么寫出來是不是有問題?這直接減法減出來的數據應該不對,是的,結果肯定不對。
- 0.6000000000000001
為什么運算結果有問題呢?那加法和乘法是不是都會有這種問題,恭喜你,想到了,確實會有這種問題,而這個問題,就得從我們的計算機去開始討論了,計算機并不能識別除了二進制數據以外的任何數據。也就是說,我們傳遞給計算機的是十進制的數據,但是計算機需要先把我們給的數據轉換成二進制的數據,因為不能直接識別十進制的數據,這時候,2.0 是十進制的數據,轉換成二進制的數據,而1.4呢?轉換成二進制的數據反而出現了問題 1.4在二進制中,則是會出現1.399999。。。這樣的數據,當我們進行數據轉換的時候,就出現了2.0-1.399999這樣的數據。
這個時候就有人問了,我定義 float 類型為 1.4 的時候為什么不是 1.399999999呢?這就是不進行浮點計算的時候,在十進制里浮點數能正確顯示。也就是說,你如果知識定義了類型為 float 的話,但是你不用這個數字去進行計算,那就沒問題,但是一旦參與了運算,那就不行了,分分鐘被diss。
阿里手冊定義
數據庫 小數類型為 decimal,禁止使用 float 和 double。
在存儲的時候,float 和 double 都存在精度損失的問題,很可能在比較值的時候,得到不正確的 結果。如果存儲的數據范圍超過 decimal 的范圍,建議將數據拆成整數和小數并分開存儲。
Java程序:使用 BigDecimal 來定義值,再進行浮點數的運算操作
BigDecimal 是 Java 在 java.math 包中提供的API類,用來對超過16位有效位的數進行精確的運算
使用 BigDecimal 要注意的東西
1.BigDecimal(double) 創建一個具有參數所指定雙精度值的對象
但是這種類型是都不推薦使用的,為什么不推薦使用,我們來試一下
- BigDecimal bigDecimal = new BigDecimal(0.2);
- System.out.println(bigDecimal);
當你寫出這段代碼的時候,感覺沒啥問題,當輸出出來的時候,就懵了。
- 0.200000000000000011102230246251565404236316680908203125
又出現精度問題了?其實當你在點擊到這個方法看源碼的時候,注釋都提醒你慎重了。
- * The results of this constructor can be somewhat unpredictable. 這個構造函數可以有些不可預測的結果
- * One might assume that writing {@code new 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
- * {@code double} (or, for that matter, as a binary fraction of
- * any finite length). Thus, the value that is being passed
- * <i>in</i> to the constructor is not exactly equal to 0.1,
- * appearances notwithstanding.
阿粉看到第一句話的時候,就知道,以后別用 double 數據類型去初始化這個 bigDecimal 了,不靠譜呀。
也就是說存在精度損失風險,在精確計算或值比較的場景中可能會導致業務邏輯異常
既然不推薦使用 BigDecimal(double)。那么推薦使用什么呢?
BigDecimal(string) 或者使用 valueof
- BigDecimal bigDecimal = new BigDecimal("0.2");
- System.out.println(bigDecimal);
- BigDecimal bigDecimal1 = BigDecimal.valueOf(0.2);
- System.out.println(bigDecimal1);
這時候,我們再來看看是否和我們預期的結果是一樣的。
- 0.2
- 0.2
這兩個實際上都是一個,valueof 只不過是在源碼中幫我們把 double 給變換成了 Double.toString(val) ,也就是還是string。
這就是為什么有些面試官在面試基礎的時候,很多次會問,float 和 double 都會丟失精度,BigDecimal 會丟失精度么?為什么?
你如果回答不會丟失精度,那恭喜你,你涼了,如果你回答會丟失精度,那么面試官肯定會追問到什么情況會丟失精度,什么情況不會丟失精度。
這也是為什么在 Effective Java 和 Mysql 必會內容 書中都會提到這塊內容,如果你是一個幾年工作經驗的人,就不會有這種錯誤,但是你初入職場,經驗沒那么多,基礎沒那么牢固的肯定會發生這種事,趕快去檢查一下你們公司的代碼吧。
BigDecimal 的加減乘除
- 加法:add
- 減法:subtract
- 乘法:multiply
- 除法:divide
BigDecimal保留小數點問題
ROUND_DOWN :向零方向舍入
ROUND_UP :向遠離0的方向舍入
ROUND_CEILING:向正無窮方向舍入
ROUND_FLOOR :向負無窮方向舍入
ROUND_HALF_DOWN:相當于五舍六入
ROUND_HALF_UP:相當于四舍五入(經常使用)
以上就是阿粉想給大家說的關于 BigDecimal 的內容了,你要去看看你公司的代碼么?