概念
BigDecimal是一個不可變的,任意精度的有符號十進制數(shù)
BigDecimal由任意精度的整數(shù)非標度值和32位的整數(shù)標度(scale)組成。如果為零或整數(shù)樟澜,則標度是小數(shù)點后的位數(shù)误窖。如果為負數(shù),則將該數(shù)的非標度值乘以10的負scale次冪秩贰。因此霹俺,BigDecimal表示的數(shù)值是(unscaledValueX10 ̄scale)。
BigDecimal對象內通過BigInteger IntVal存儲傳遞對象數(shù)字部分毒费,通過int scale記錄小數(shù)點位數(shù)丙唧,通過int precision記錄有效位數(shù)(默認為0)。
BigDecimal的加減乘除就成了BigInteger與BigInteger之間的加減乘除觅玻,浮點數(shù)的計算也轉化為整形的計算想际,可以大大提供性能,并且通過BigInteger可以保存大數(shù)字溪厘,從而實現(xiàn)真正十大進制的計算胡本,在整個計算過程中,還涉及scale的判斷和precision判斷從而確定最終輸出結果畸悬。
????????????在Java中侧甫,由CPU原生提供的整型最大范圍是64位long型整數(shù)。使用long型整數(shù)可以通過CPU指令進行計算蹋宦,速度會非撑冢快。果使用的整數(shù)范圍超過了long型冷冗,BigInteger就是用來表示任意大小的整數(shù)守屉,BigInteger內部用一個int[]數(shù)組來模擬一個非常大的數(shù)。
類中屬性
scale:有多少位小數(shù)(即小數(shù)點后有多少位)
precision:一共有多少位數(shù)字蒿辙,在確定了precision后就會要求結合Rounding Mode做一些舍入方面的操作
intVal和scale胸梆,分別表示BigDecimal的無標度值和標度敦捧,BigDecimal可以表示為一個任意精度的無標度值和一個32位整型的標度
intCompact:字符串去掉小數(shù)點后,轉為long的值碰镜,如果intVal在compact的過程中超過了Long.MAX_VAULE則將intCompact記為Long.MIN_VALUE
intVal:當傳的字符串長度大于等于18時才使用BigInteger表示數(shù)字
stringCache:在toString方法的時候用到
BigDecimal 繼承了Number并實現(xiàn)了Comparable接口兢卵。
繼承了Number,將會提供將表示的數(shù)值轉換為byteValue()绪颖、shortValue()秽荤、intValue()、longValue()柠横、floatValue()窃款、doubleValue()的方法
實現(xiàn)了Comparable接口,可以使用compareTo()方法來進行比較
問題:
使用newBigDecimal(double)的方式計算精確數(shù)值牍氛,會有精度缺失的問題晨继,使用BigDecimal.valueOf(double)的方式就不會存在此問題。
具體原因:
float與double類型主要是為了科學計算和工程計算而設計搬俊。為了在廣泛的數(shù)值范圍上提供較為精確的快速近和計算而精心設計的紊扬,并沒有完全精確的結果,所以不應該被用于精確結果的場合唉擂。
????????new BigDecimal()源碼
BigDecimal.valueOf()源碼
不同的地方在于valueOf()方法在對double類型轉換的時候餐屎,做了一次轉換為字符串的操作,從而避免了不準確的問題玩祟;但是如果是float類型腹缩,那么此方法又會出現(xiàn)不準確的問題。
參數(shù)類型為int空扎,long藏鹊,字符串時,這兩種方式?jīng)]有任何區(qū)別转锈。
正確的做法是自己構造BigDecimal對象時伙判,使用String.valueOf()將參數(shù)顯示轉換為字符串。官方注釋也有說明此問題黑忱,使用new BigDecimal(double val)方法得到的結果是不可預知的宴抚,推薦使用入?yún)㈩愋蜑镾tring的構造函數(shù)來進行浮點數(shù)的精確計算。
當入?yún)㈩愋蜑閒loat時甫煞,使用valueOf()方法以及new BigDecimal()構造方法會出現(xiàn)浮點運算精度不一致的問題菇曲。使用String.valueOf(String val)方法,運算結果正確抚吠。
valueOf()方法對參數(shù)進行一次double的轉換常潮,9.9f會被強制轉換為double類型,所以會出現(xiàn)精度問題楷力。
BigDecimal比較問題(equals與compareTo)
問題:當入?yún)閟tring類型時喊式,會出現(xiàn)此問題孵户。如果入?yún)㈩愋筒粸閟tring類型,并不會出現(xiàn)此問題岔留,但是會有精度不準確的影響夏哭。
使用BigDecimal(“0.00”).equals(BigDecimal.ZERO)與BigDecimal(0.00).equals(BigDecimal.ZERO)區(qū)別
解決方案:可以使用compareTo(BigDecimal.ZERO)==0,來判斷是否等于0
結論:對于BigDecimal的大小比較献联,使用equals方法的話不僅會比較值的大小竖配,還會比較兩個對象的精確度,而compareTo方法數(shù)值相同精度不同也會被視認為相等里逆。
compareTo()方法中的注釋有所體現(xiàn)进胯。
使用方式案例
常用構造函數(shù)
BigDecimal(int):創(chuàng)建一個具有參數(shù)所指定整數(shù)值的對象
BigDecimal(double):創(chuàng)建一個具有參數(shù)所指定雙精度值的對象
BigDecimal(long):創(chuàng)建一個具有參數(shù)所指定長整數(shù)值的對象
Bigdecimal(String):創(chuàng)建一個具有參數(shù)所指定以字符串表示的數(shù)值的對象
常用方法
add(BigDecimal):BigDecimal對象中的值相加,返回BigDecimal對象
substract(BigDecimal):BigDecimal對象中的值相減原押,返回BigDecimal對象
multiply(BigDecimal):BigDecimal對象中的值相乘胁镐,返回BigDecimal對象
divide(BigDecimal):相除
doubleValue():轉雙精度
longValue():轉長整型
intValue():轉整型
floatValue():轉單精度
toString():將BigDecimal對象中的值轉換成字符串
BigDecimal大小比較
BigDecimal比較大小一般用的是compareTo方法
BigDecimal big=new BigDecimal(5.2);
int i = big.compareTo(new BigDecimal(2.2));
System.out.println(i);
BigDecimal.ROUND_DOWN
ROUND_DOWN:直接省略掉指定位數(shù)后的內容
BigDecimal b=new? ? BigDecimal("2.2345").setScale(2,Bigdecimal.ROUND_DOWN); // 2.23
BigDecimal.ROUND_UP
// 直接對指定位數(shù)后的內容做進一位處理
BigDecimal b=new BigDecimal("2.2355").setScale(2,Bigdecimal.ROUND_DOWN);// 2.24
BigDecimal.ROUND_CEILING
// 正數(shù)使用ROUND_UP規(guī)則,負數(shù)使用ROUND_DOWN規(guī)則
BigDecimal b=new BigDecimal("2,125446").setScale(2,BigDecimal.ROUND_CEILING);// 2.13
BigDecimal b=new BigDecimal("-2.1253456").setScale(2,BigDecimal.ROUND_CEILING);// -2.12
BigDecimal.ROUND_FLOOR
// 正數(shù)省略內容诸衔,負數(shù)向下進一位
BigDecimal b=new BigDecimal("2.125456").setScale(2,BigDecimal.ROUND_FLOOR);// 2.12
BigDecimal b=new BigDecimal("-2.125456").setScale(2,BigDecimal.ROUND_FLOOR);// -2.13
BigDecimal.ROUND_HALF_UP BigDecimal.ROUND_HALF_DOWN 四舍五入
BigDecimal b=new BigDecimal("2.125456").setScale(2,BigDecimal.ROUND_HALF_UP);// 2.13
BigDecimal b=new BigDecimal("-2.125456").setScale(2,BigDecimal.ROUND_HALF_DOWN);// -2.13
BigDecimal.ROUND_HALF_EVEN
指定小數(shù)位的前一位盯漂,如果是奇數(shù)則四舍五入后進位,如果是偶數(shù)則舍棄指定小數(shù)位后面內容
BigDecimal bigDecimalA = new BigDecimal("2.113").setScale(2, BigDecimal.ROUND_HALF_EVEN);
// 2.11? 雖然是奇數(shù)署隘,但是3<5宠能,不會進位
BigDecimal bigDecimalB = new BigDecimal("2.115").setScale(2, BigDecimal.ROUND_HALF_EVEN);
// 2.12? 因為是奇數(shù)且符合"五入"亚隙,則進位
此舍入模式也稱為“銀行家舍入法”磁餐,主要在美國使用。四舍六入阿弃,五分兩種情況诊霹。
如果前一位為奇數(shù),則入位渣淳,否則舍去脾还。
ROUND_UNNECESSARY
斷言請求的操作具有精確的結果,因此不需要舍入
如果對獲得精確結果的操作指定此舍入模式入愧,則拋出ArithmeticException
其他注意問題
在使用divide方法進行除法時當不整除鄙漏,出現(xiàn)無限循環(huán)小數(shù)時,就會拋異常棺蛛。
解決方法:divide方法設置精確的小數(shù)點怔蚌,divide(xxxxx,2)
小總結
不要隨便使用BigDecimal,一般精度的計算沒必要使用BigDecimal。因為BigDecimal的精度比double和float性能差旁赊。在處理龐大復雜的計算尤為明顯桦踊。
盡量使用參數(shù)類型為String的構造函數(shù)
BigDecimal都是不可變的(immutable)的,在進行每一次四則運算時终畅,都會產(chǎn)生一個新的對象籍胯,所以在做加減乘除運算時要記得保存操作后的值