Java BigDecimal初探
更新時(shí)間:2016-03-17
一、引言
《Effactive Java》中有這樣的描述:float和double類(lèi)型的主要設(shè)計(jì)目標(biāo)是為了科學(xué)計(jì)算和工程計(jì)算。他們執(zhí)行二進(jìn)制浮點(diǎn)運(yùn)算,這是為了在廣域數(shù)值范圍上提供較為精確的快速近似計(jì)算而精心設(shè)計(jì)的。然而,它們沒(méi)有提供完全精確的結(jié)果,所以不應(yīng)該被用于要求精確結(jié)果的場(chǎng)合。但是,貨幣計(jì)算往往要求結(jié)果精確,這時(shí)候可以使用int、long或BigDecimal。
二、不可變性
BigDecimal是不可變類(lèi),每一個(gè)操作(加減乘除等)都會(huì)返回一個(gè)新的對(duì)象, 下面以加法操作為例:
BigDecimal a =new BigDecimal("1.22"); System.out.println("construct with a String value: " + a); BigDecimal b =new BigDecimal("2.22"); a.add(b); System.out.println("a plus b is : " + a);我們很容易會(huì)認(rèn)為會(huì)輸出:
construct with a String value: 1.22 a plus b is :3.44但實(shí)際上a plus b is : 1.22
因?yàn)锽igDecimal是不可變的(immutable)的,在進(jìn)行每一步運(yùn)算時(shí),都會(huì)產(chǎn)生一個(gè)新的對(duì)象,所以a.add(b)雖然做了加法操作,但是a并沒(méi)有保存加操作后的值,正確的用法應(yīng)該是a=a.add(b); 減乘除操作也是一樣的返回一個(gè)新的BigDecimal對(duì)象。
三、構(gòu)造函數(shù)和valueOf方法
首先看如下一段代碼:
// use constructor BigDecimal(double) BigDecimal aDouble =new BigDecimal(1.22); System.out.println("construct with a double value: " + aDouble);// use constructor BigDecimal(String) BigDecimal aString = new BigDecimal("1.22"); System.out.println("construct with a String value: " + aString);// use constructor BigDecimal.valueOf(double) BigDecimal aValue = BigDecimal.valueOf(1.22); System.out.println("use valueOf method: " + aValue);你認(rèn)為輸出結(jié)果會(huì)是什么呢?如果你認(rèn)為第一個(gè)會(huì)輸出1.22,那么恭喜你答錯(cuò)了,輸出結(jié)果如下:
construct with a double value: 1.2199999999999999733546474089962430298328399658203125 construct with a String value: 1.22 use valueOf method: 1.22為什么會(huì)這樣呢?JavaDoc對(duì)于BigDecimal(double)有很詳細(xì)的說(shuō)明:
1、參數(shù)類(lèi)型為double的構(gòu)造方法的結(jié)果有一定的不可預(yù)知性。有人可能認(rèn)為在Java中new BigDecimal(0.1)所創(chuàng)建的BigDecimal的值正好等于 0.1(非標(biāo)度值 1,其標(biāo)度為 1),但是它實(shí)際上等于0.1000000000000000055511151231257827021181583404541015625。這是因?yàn)?.1無(wú)法準(zhǔn)確地表示為 double(或者說(shuō)對(duì)于該情況,不能表示為任何有限長(zhǎng)度的二進(jìn)制小數(shù))。這樣,傳入到構(gòu)造方法的值不會(huì)正好等于 0.1(雖然表面上等于該值)。
2、另一方面,String 構(gòu)造方法是完全可預(yù)知的:new BigDecimal("0.1") 將創(chuàng)建一個(gè) BigDecimal,它的值正好等于期望的0.1。因此,比較而言,通常建議優(yōu)先使用String構(gòu)造方法。
3、當(dāng) double必須用作BigDecimal的來(lái)源時(shí),請(qǐng)注意,此構(gòu)造方法提供了一個(gè)精確轉(zhuǎn)換;它不提供與以下操作相同的結(jié)果:先使用Double.toString(double)方法將double轉(zhuǎn)換為String,然后使用BigDecimal(String)構(gòu)造方法。要獲取該結(jié)果,使用static valueOf(double)方法。
BigDecimal.valueOf(double) 使用由 Double.toString(double)方法提供的 double的標(biāo)準(zhǔn)化字符串表示形式( canonical string representation) 將 double轉(zhuǎn)換成 BigDecimal 。這也是比較推薦的一種方式。
BigDecimal.valueOf(double)還有一個(gè)重載的方法 BigDecimal.valueOf(long),對(duì)于某些常用值(0到10) BigDecimal在內(nèi)部做了緩存, 如果傳遞的參數(shù)值范圍為[0, 10], 這個(gè)方法直接返回緩存中相應(yīng)的BigDecimal對(duì)象。
四、equals方法
BigDecimal.equals方法是有問(wèn)題的。僅當(dāng)你確定比較的值有著相同的標(biāo)度時(shí)才可使用。因此,當(dāng)你校驗(yàn)相等性時(shí)注意BigDecimal有一個(gè)標(biāo)度,用于相等性比較。而compareTo方法則會(huì)忽略這個(gè)標(biāo)度(scale)。
參見(jiàn)以下測(cè)試代碼:
// 打印false System.out.println(new BigDecimal("0.0").equals(new BigDecimal("0.00")));// 打印false System.out.println(new BigDecimal("0.0").hashCode() == (new BigDecimal("0.00")).hashCode());// 打印0 System.out.println(new BigDecimal("0.0").compareTo(new BigDecimal("0.00")));五、對(duì)除法使用標(biāo)度
BigDecimal對(duì)象的精度沒(méi)有限制。如果結(jié)果不能終止,divide方法將會(huì)拋出ArithmeticException, 如1 / 3 = 0.33333...。所以強(qiáng)烈推薦使用重載方法divide(BigDecimal d, int scale, int roundMode)指定標(biāo)度和舍入模式來(lái)避免以上異常。
關(guān)于舍入模式常用的有BigDecimal.ROUND_HALF_UP,也就是四舍五入。
六、總結(jié)
1、商業(yè)計(jì)算(要求精確結(jié)果)時(shí)使用BigDecimal。
2、使用參數(shù)類(lèi)型為String的構(gòu)造函數(shù),將double轉(zhuǎn)換成BigDecimal時(shí)用BigDecimal.valueOf(double),做除法運(yùn)算時(shí)使用重載的方法divide(BigDecimal d, int scale, int roundMode)。
3、BigDecimal是不可變的(immutable)的,在進(jìn)行每一步運(yùn)算時(shí),都會(huì)產(chǎn)生一個(gè)新的對(duì)象,所以在做加減乘除運(yùn)算時(shí)千萬(wàn)要保存操作后的值。
4、盡量使用compareTo方法比較兩個(gè)BigDecimal對(duì)象的大小。
轉(zhuǎn)載于:https://www.cnblogs.com/liycode/p/9306361.html
總結(jié)
以上是生活随笔為你收集整理的Java BigDecimal初探的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。