bigdecimal取小数部分_小数精度丢失问题分析和解决
無(wú)論在什么業(yè)務(wù)中,錢(qián)?是非常重要的東西,對(duì)賬的時(shí)候一定要對(duì)的上,不能這邊少一分錢(qián)那邊多一分錢(qián)。對(duì)于數(shù)值的計(jì)算,尤其是小數(shù),floate和double都是禁止使用的。
阿里強(qiáng)制要求存放小數(shù)時(shí)使用 decimal,禁止使用 float 和 double。
說(shuō)明:float 和 double 在存儲(chǔ)的時(shí)候,存在精度損失的問(wèn)題,很可能在值的比較時(shí),得到不正確的結(jié)果。如果存儲(chǔ)的數(shù)據(jù)范圍超過(guò) decimal 的范圍,建議將數(shù)據(jù)拆成整數(shù)和小數(shù)分開(kāi)存儲(chǔ)。
處理方式可以為:mysql?可以用?decimal?,如果你是用?java, 在商業(yè)計(jì)算中我們要用?java.math.BigDecimal,注意:如果需要精確計(jì)算,非要用String來(lái)構(gòu)造BigDecimal不可!
那么到底是什么情況?為什么我們的賬戶一會(huì)少一分一會(huì)多一分(往往是少一分),如何解決呢?
一個(gè)例子說(shuō)明
廢話不多說(shuō),當(dāng)我們拿著一塊錢(qián)去買(mǎi)了一根9毛的棒冰會(huì)發(fā)生啥?本來(lái)只剩1毛錢(qián)就不多了,老板還扣我0.000....0002分?上圖:
問(wèn)題原因
無(wú)論是我們本文提到的double,還是float,都是浮點(diǎn)數(shù)。
在計(jì)算機(jī)科學(xué)中,浮點(diǎn)(英語(yǔ):floating point,縮寫(xiě)為FP)是一種對(duì)于實(shí)數(shù)的近似值數(shù)值表現(xiàn)法,由一個(gè)有效數(shù)字(即尾數(shù))加上冪數(shù)來(lái)表示,通常是乘以某個(gè)基數(shù)的整數(shù)次指數(shù)得到。以這種表示法表示的數(shù)值,稱為浮點(diǎn)數(shù)(floating-point number)。
劃重點(diǎn),???其實(shí)我覺(jué)得很好理解,我們之前說(shuō)過(guò),計(jì)算機(jī)計(jì)算加減乘除啊,都是用的加法器,實(shí)質(zhì)都是二進(jìn)制的加法處理。那么這里就有一個(gè)二進(jìn)制表示的問(wèn)題。試想,4,2,8之流都是2的冪次方,可以完美用二進(jìn)制表示,計(jì)算當(dāng)然不會(huì)出現(xiàn)問(wèn)題。對(duì)于0,1,3,5之類也都可以用二進(jìn)制來(lái)表示出來(lái),所以,整數(shù)肯定是沒(méi)問(wèn)題的。
但是對(duì)于小數(shù)呢?1(2的0次方)、0.5(2的-1次方)、0.25(2的-2次方)、0.75(2的-1次方+2的-2次方),那都是可以轉(zhuǎn)換成二進(jìn)制的小數(shù):
但是如十進(jìn)制的0.1,就無(wú)法用二進(jìn)制準(zhǔn)確的表示出來(lái)(你用2的次方來(lái)湊湊?)。因此只能使用近似值的方式表達(dá)。如果我們嘗試著把10進(jìn)制的0.1轉(zhuǎn)化成二進(jìn)制,會(huì)怎么轉(zhuǎn)呢?
在十進(jìn)制中,0.1如何計(jì)算出來(lái)的呢?
0.1 = 1 ÷ 10
那么二進(jìn)制中也是同理:
1 ÷ 1010
我們回到小學(xué)的課堂,來(lái)列豎式吧:
0.000110011...------------------
1010 ) 1 0000
1010
------
1100
1010
----
10000
1010
-----
1100
1010
----
10
很顯然,除不盡,除出了一個(gè)無(wú)限循環(huán)小數(shù):二進(jìn)制的 0.0001100110011...
有的同學(xué)表示懷疑?這結(jié)果正確?
我寫(xiě)在這里當(dāng)然正確啦,前面標(biāo)注了是二進(jìn)制,小數(shù)點(diǎn)后面一位就是-1次方依次計(jì)算,我們的0.1是不是介于(2的-3次方)和(2的-4次方)之間,那么顯然是從小數(shù)點(diǎn)第四個(gè)開(kāi)始有1。
好了,那么,如何在計(jì)算機(jī)中表示這個(gè)無(wú)限不循環(huán)的小數(shù)呢?只能考慮按照不同的精度保留不同的位數(shù)。
我們知道float是單精度的(JAVA中是32位),double是雙精度的(JAVA中是64位)。不同的精度,其實(shí)就是保留的有效數(shù)字位數(shù)不同,保留的位數(shù)越多,精度越高。
所以,浮點(diǎn)數(shù)在Java中是無(wú)法精確表示的,因?yàn)榇蟛糠指↑c(diǎn)數(shù)轉(zhuǎn)換成二進(jìn)制是一個(gè)無(wú)限不循環(huán)的小數(shù),只能通過(guò)保留精度的方式進(jìn)行近似表示。
問(wèn)題的解決
String?構(gòu)造方法是完全可預(yù)知的:寫(xiě)入?newBigDecimal("0.1")?將創(chuàng)建一個(gè)?BigDecimal,它正好等于預(yù)期的 0.1。因此,比較而言,通常建議優(yōu)先使用String構(gòu)造方法。
使用BigDecimal(String val)!
//加法public static BigDecimal add(double v1, double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2);
}
//減法
public static BigDecimal sub(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2);
}
//乘法
public static BigDecimal mul(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2);
}
//除法
public static BigDecimal div(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2,2,BigDecimal.ROUND_HALF_UP);//四舍五入,保留2位小數(shù),應(yīng)對(duì)除不盡的情況
}
那么,上面的精度丟失問(wèn)題就迎刃而解了。但是除不盡怎么辦?比如10.0除以這里的3.0,保留小數(shù)點(diǎn)后三位有效數(shù)字:
那么,每個(gè)用戶得到的都是3.333元,三個(gè)用戶加起來(lái)是得不到10塊錢(qián)的。
對(duì)于除法,始終會(huì)產(chǎn)生除不盡的情況怎么辦?有個(gè)詞叫軋差
什么意思呢?舉個(gè)簡(jiǎn)單例子。假如現(xiàn)在需要把10元分成3分,如果是10除以3這么除,會(huì)發(fā)現(xiàn)為3.33333無(wú)窮盡的3。這些數(shù)字完全無(wú)法在程序或數(shù)據(jù)庫(kù)中進(jìn)行精確的存儲(chǔ)。
簡(jiǎn)單理解就是,當(dāng)除不盡或需去除小數(shù)點(diǎn)的時(shí)候,前面的n-1筆(這里n=3)做四舍五入。最后一筆做兜底(總金額減去前面n-1筆之和)。這樣保證總金額的不會(huì)丟失。
比如10塊錢(qián),三個(gè)用戶分,前面兩個(gè)用戶只能各分到3.333塊錢(qián),最后一個(gè)用戶分到3.334塊錢(qián)。保證總額不變。是不是感覺(jué)很機(jī)智
好了,我們可以準(zhǔn)確地管理我們的錢(qián)了。不過(guò)針對(duì)這個(gè)錢(qián)的問(wèn)題,更機(jī)智的我選擇保存錢(qián)的時(shí)候用分為單位來(lái)操作和保存,能少一事就少一事吧。當(dāng)然了,有的時(shí)候必須用到小數(shù)的時(shí)候,請(qǐng)記住我們?cè)撊绾问褂门秪
總結(jié)
以上是生活随笔為你收集整理的bigdecimal取小数部分_小数精度丢失问题分析和解决的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: js 取得数组下标_数组的介绍及使用
- 下一篇: 有什么用_app用什么软件编写