BigDecimal的引入
在利用Java編程語言開發(fā)銀行、金融類等需要對數(shù)值進(jìn)行高精度計(jì)算的軟件時(shí)蔚出,我們經(jīng)常使用BigDecimal
和BigInteger
這兩個(gè)大數(shù)類弟翘,而不是常見的int、long骄酗、float稀余、double類型,特別是在處理浮點(diǎn)型數(shù)據(jù)趋翻。
我們先看一下使用基礎(chǔ)數(shù)據(jù)類型double進(jìn)行計(jì)算并打印結(jié)果的一個(gè)代碼演示:
public class MainClass {
public static void main(String[] args) {
System.out.println(0.02+0.01);
System.out.println(0.05+0.01);
}
}
結(jié)果如下:
0.03
0.060000000000000005
問題來了睛琳,為什么會(huì)出現(xiàn)第二種結(jié)果的數(shù)據(jù)呢?根本原因還是我們的計(jì)算機(jī)是由二進(jìn)制的踏烙,而二進(jìn)制是沒辦法來精確表示一個(gè)浮點(diǎn)數(shù)师骗,CPU采用“尾數(shù)和指數(shù)”的方式(科學(xué)計(jì)數(shù)法)表達(dá)浮點(diǎn)數(shù)的時(shí)候存在一定的誤差。所以讨惩,當(dāng)對數(shù)據(jù)精度要求比較高的時(shí)候辟癌,還是需要采用BigDecimal類,盡管計(jì)算速度上稍微慢了一些荐捻。
BigDecimal的使用
創(chuàng)建一個(gè)BigDecimal對象有構(gòu)造函數(shù)和公有靜態(tài)方法(BigDecimal.valueOf)兩種方式黍少,需要注意兩點(diǎn):
構(gòu)造函數(shù)包含使用基本數(shù)據(jù)類型和字符串作為參數(shù)的兩種形式,推薦使用后者处面,如:
new BigDecimal(Double.valueOf(0.09))
厂置。大家可以嘗試一下,System.out.println(new BigDecimal(0.06).toString());
語句的輸出結(jié)果是:0.059999999999999997779553950749686919152736663818359375Decimal打印日志或向基本數(shù)據(jù)類型轉(zhuǎn)換時(shí)魂角,盡量使用它提供的公有方法xxxValue()昵济,比如doubleValue(),而不是簡單粗暴的一個(gè)toString()野揪。
BigDecimal舍入模式
盡管數(shù)據(jù)庫存儲(chǔ)的是一個(gè)高精度的浮點(diǎn)數(shù)访忿,但是通常在應(yīng)用中展示的時(shí)候往往需要限制一下小數(shù)點(diǎn)的位數(shù),比如兩到三位小數(shù)即可囱挑,這時(shí)就需要使用到setScale(int newScale, int roundingMode)
函數(shù)醉顽,作為BigDecimal的公有靜態(tài)變量,舍入模式(Rounding Mode)的運(yùn)算規(guī)則比較多平挑,公有八種游添,這里作個(gè)說明,官方文檔也有介紹通熄。
ROUND_UP
向遠(yuǎn)離零的方向舍入唆涝。舍棄非零部分,并將非零舍棄部分相鄰的一位數(shù)字加一唇辨。ROUND_DOWN
向接近零的方向舍入廊酣。舍棄非零部分,同時(shí)不會(huì)非零舍棄部分相鄰的一位數(shù)字加一赏枚,采取截取行為亡驰。ROUND_CEILING
向正無窮的方向舍入晓猛。如果為正數(shù),舍入結(jié)果同ROUND_UP一致凡辱;如果為負(fù)數(shù)戒职,舍入結(jié)果同ROUND_DOWN一致。注意:此模式不會(huì)減少數(shù)值大小透乾。ROUND_FLOOR
向負(fù)無窮的方向舍入洪燥。如果為正數(shù),舍入結(jié)果同ROUND_DOWN一致乳乌;如果為負(fù)數(shù)捧韵,舍入結(jié)果同ROUND_UP一致。注意:此模式不會(huì)增加數(shù)值大小汉操。ROUND_HALF_UP
向“最接近”的數(shù)字舍入再来,如果與兩個(gè)相鄰數(shù)字的距離相等,則為向上舍入的舍入模式客情。如果舍棄部分>= 0.5其弊,則舍入行為與ROUND_UP相同;否則舍入行為與ROUND_DOWN相同膀斋。這種模式也就是我們常說的我們的“四舍五入”梭伐。ROUND_HALF_DOWN
向“最接近”的數(shù)字舍入,如果與兩個(gè)相鄰數(shù)字的距離相等仰担,則為向下舍入的舍入模式糊识。如果舍棄部分> 0.5,則舍入行為與ROUND_UP相同摔蓝;否則舍入行為與ROUND_DOWN相同赂苗。這種模式也就是我們常說的我們的“五舍六入”。ROUND_HALF_EVEN
向“最接近”的數(shù)字舍入贮尉,如果與兩個(gè)相鄰數(shù)字的距離相等拌滋,則相鄰的偶數(shù)舍入。如果舍棄部分左邊的數(shù)字奇數(shù)猜谚,則舍入行為與 ROUND_HALF_UP 相同败砂;如果為偶數(shù),則舍入行為與 ROUND_HALF_DOWN 相同魏铅。注意:在重復(fù)進(jìn)行一系列計(jì)算時(shí)昌犹,此舍入模式可以將累加錯(cuò)誤減到最小。此舍入模式也稱為“銀行家舍入法”览芳,主要在美國使用斜姥。四舍六入,五分兩種情況,如果前一位為奇數(shù)铸敏,則入位缚忧,否則舍去。ROUND_UNNECESSARY
斷言請求的操作具有精確的結(jié)果搞坝,因此不需要舍入碎乃。如果對獲得精確結(jié)果的操作指定此舍入模式赂弓,則拋出ArithmeticException蹬竖。
下面薪棒,舉個(gè)例子說明一下不同舍入模式下的數(shù)值計(jì)算結(jié)果厦画,保留一位小數(shù):
INPUT_NUM | UP | DOWN | CEILING | FLOOR | HALF_UP | HALF_DOWN | HALF_EVEN | UNNECESSARY |
---|---|---|---|---|---|---|---|---|
5.5 | 6 | 5 | 6 | 5 | 6 | 5 | 6 | Exception |
2.5 | 3 | 2 | 3 | 2 | 3 | 2 | 2 | Exception |
1.6 | 2 | 1 | 2 | 1 | 2 | 2 | 2 | Exception |
1.1 | 2 | 1 | 2 | 1 | 1 | 1 | 1 | Exception |
1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | Exception |
-1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | Exception |
-1.1 | -2 | -1 | -1 | -2 | -1 | -1 | -1 | Exception |
-1.6 | -2 | -1 | -1 | -2 | -2 | -2 | -2 | Exception |
-2.5 | -3 | -2 | -2 | -3 | -3 | -2 | -2 | Exception |
-5.5 | -6 | -5 | -5 | -6 | -6 | -5 | -6 | Exception |