bigdecimal 保留两位小数_一律使用 BigDecimal,避免后患?
你知道的越多,不知道的就越多,業余的像一棵小草!
你來,我們一起精進!你不來,我和你的競爭對手一起精進!
編輯:業余草
zhuanlan.zhihu.com/p/94144867
推薦:https://www.xttblog.com/?p=5116
一律使用 BigDecimal,避免后患?
一、背景
總在項目中看到 Double 與 BigDecimal 被用錯的情況,竟然有人告訴我:“一律使用 BigDecimal,避免后患”,我相信這位兄弟肯定是被精度問題搞蒙了,因此我想同步一下我的使用姿勢,僅提供參考。
二、兩種類型使用過程中都有可能出坑
double 的小數部分容易出現使用二進制無法準確表示。
如十進制的0.1,0.2,0.3,0.4 都不能準確表示成二進制。
具體原因相信大家都懂,我就不多說了。如果有不懂的可以私信我或者評論留言!
dobule 小數超過 15 位的相等比較就不一定對了。都是 true。
所以,使用 BigDecimal 進行除法運算一定要有精度!
一般情況下都不使用 new BigDecimal(double) 應該使用 BigDecimal.valueOf(double)。
BigDecimal?d1?=?BigDecimal.valueOf(12.3);//結果是12.3?你預期的
BigDecimal?d2?=?new?BigDecimal(12.3);
//結果是12.300000000000000710542735760100185871124267578125
我想 12.300000000000000710542735760100185871124267578125 肯定不是你想要的結果,因此 new BigDecimal(double) 可能會產生不是你預期的結果,原理可以自行看一下底層源代碼,還是比較容易搞懂的。
另:BigDecimal.valueOf(xxx) 是靜態工廠類,永遠優先于構造函數(摘自《Effecitve java》,此書也是非常推薦的一本經典書)
如原來 d1=1.11 ,又加了一個數 2.11,這種操作要注意結果要指向新對象。
任何針對BigDecimal對象的修改都會產生一個新對象;
BigDecimal?newValue?=?BigDecimal.valueOf(1.2222).add(BigDecimal.valueOf(2.33333));BigDecimal?newValue?=?BigDecimal.valueOf(1.2222).setScale(2);
總之每次修改都要重新指向新對象,才能保證計算結果是對的。
比較大小和相等都使用 compareTo,如果需要返回大數或小數可使用 max,min。且注意不能使用 equals。
三、效率比較
比如:1 累加到 1000000(以本人機器 MacBookPro 2018 i7 2.2G)double 比 BigDecimal 快大約 10 倍
double: 2ms
BigDecimal:16ms
四、優缺點總結
double 的優缺點:
double在計算過程中容易出現丟失精度問題
使用方便,有包裝類,可自動拆裝箱,計算效率高
BigDecimal 的優缺點:
精度準確,但做除法時要注意除不盡的異常
BigDecimal 是對象類型,也沒有自動拆封箱機制,操作起來總是有些不順手
五、使用場景推薦
涉及到精準計算如金額,一定要使用 BigDecimal 或轉成 long 或 int 計算。
若不需要精準的,如一些統計值:(本身就沒有精確值)。
用戶平均價格,店鋪評分,用戶經緯度等本就沒有精準值一說的推薦使用 double 或 float,寫代碼更方便,計算效率也高得多。
值得一提的說,如果 double 或 float 僅是用于傳值,并不會有精度問題,但如果參與了計算就要小心了。要區分是不是需要精準值,如果需要精準值,需要轉成 BigDecimal 計算以后再轉成 double。
但依然約定在 DTO 定義金額時使用 BigDecimal 或整形值,是為了減少或避免 double 參與金額計算的機會,避免出 bug。
其他1:代碼中看到碰到讓我覺得有問題的地方
以下代碼在不同的類中抓出來的覺得用得不太恰當的地方:
代碼中真的不需要那么多地方使用 BigDecimal,相反用到 BigDecimal 的地方并不多,反而用 Double 的地方更多。以上代碼我希望的方式是:
提醒:DTO 中盡量使用包裝類,防止反系列化時 null 的造成的格式轉換異常
分析
經緯度:一般業務代碼中也不太會去計算,僅用于傳給地圖api等,經緯度一般用于計算距離,如果保留到 6 位小數時其實已經是 1 米級別的了,也滿足絕大多數場景了,因此使用 Double 是確實是可行的;
店鋪平均消費:本身就是一個歸納統計值,也一般用來比較大小做參考,因此也用不著 BigDecimal;
當前價格:這個不一樣了,為了減少 double 參與金錢計算,統一使用 BigDecimal 代替帶有小數的金額;
其他2:關于 Mysql 中如何選用這兩種類型
首先與 java 不同的是 mysql 是用來持久化數據的,而 java 中使用的數據一般更多的是過一下內存;
數據庫都要除了指定數據類型指外還需要指定精度,因此在 DB 中 Double 計算時精度的丟失比 Java 高得多;
因為 Java 默認精確到 15-16 位了;
考慮到以上與 java 中不同幾點,做點個人使用總結:
與商業金融相關字段要使用 Decimal 來表示,如金額,費率等字段;
參與各類計算如加,減,乘,除,sum,avg 等等,也要使用 Decimal;
經緯度,可以使用 double 來表示,這個可參考 Java,只要保證精度范圍即可;
如果確實不確定使用什么 double 或 Decimal 哪種類型合適,那最好使用 Decimal,畢竟穩定,安全高于一切;
注:阿里的編碼規范中強調統一帶小數的類型一律使用 Decimal 類型,也是有道理的,使用Decimal 可以大大減少計算踩坑的概率。
總結
以上是生活随笔為你收集整理的bigdecimal 保留两位小数_一律使用 BigDecimal,避免后患?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分享9个实用的电脑维修技巧,赶紧收藏吧!
- 下一篇: 成信钟楼定时微博报时的设计与实现