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