開發(fā)過程中經(jīng)常會(huì)需要進(jìn)行一些數(shù)據(jù)的運(yùn)算操作,比如加俏脊、減、乘、除,但是在計(jì)算過程中,到底使用哪種數(shù)據(jù)類型進(jìn)行計(jì)算才是最合適的呢赡模?
1田炭、數(shù)據(jù)類型
1.1、 基礎(chǔ)數(shù)據(jù)類型
- int:只能進(jìn)行整數(shù)的計(jì)算漓柑,應(yīng)用范圍單一教硫。
- float:單精度浮點(diǎn)型叨吮,可以進(jìn)行小數(shù)類型的計(jì)算,但是容易丟失精度瞬矩。
- double:雙精度浮點(diǎn)型茶鉴,精確度比float高,消耗的內(nèi)存也比float高景用,但是也容易丟失精度涵叮。
- String:字符串類型,沒法進(jìn)行運(yùn)算操作丛肢,但是恰恰要用到它和另外一種對象來進(jìn)行數(shù)據(jù)的運(yùn)算操作围肥。
- BigDecimal :Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數(shù)進(jìn)行精確的運(yùn)算蜂怎。
1.2穆刻、BigDecimal 詳細(xì)介紹
以下是關(guān)于BigDecimal 的官方文檔介紹,以及自己的一些總結(jié)杠步。涉及到BigDecimal 以及和它相關(guān)聯(lián)的MathContext氢伟、RoundingMode這兩個(gè)類。
java.math.BigDecimal
- javase官方文檔的描述:
不可變的幽歼、任意精度的有符號(hào)十進(jìn)制數(shù)朵锣。BigDecimal 由任意精度的整數(shù)非標(biāo)度值 和 32 位的整數(shù)標(biāo)度 (scale) 組成。如果為零或正數(shù)甸私,則標(biāo)度是小數(shù)點(diǎn)后的位數(shù)诚些。如果為負(fù)數(shù),則將該數(shù)的非標(biāo)度值乘以 10 的負(fù) scale 次冪皇型。因此诬烹,BigDecimal 表示的數(shù)值是 (unscaledValue × 10-scale)。
BigDecimal 類提供以下操作:算術(shù)弃鸦、標(biāo)度操作绞吁、舍入、比較唬格、哈希算法和格式轉(zhuǎn)換家破。toString() 方法提供 BigDecimal 的規(guī)范表示形式。
BigDecimal 類使用戶能完全控制舍入行為购岗。如果未指定舍入模式汰聋,并且無法表示準(zhǔn)確結(jié)果,則拋出一個(gè)異常喊积;否則烹困,通過向該操作提供適當(dāng)?shù)?MathContext 對象,可以對已選擇的精度和舍入模式執(zhí)行計(jì)算注服。在任何情況下韭邓,可以為舍入控制提供八種舍入模式。使用此類(例如溶弟,ROUND_HALF_UP)中的整數(shù)字段來表示舍入模式已過時(shí)女淑;應(yīng)改為使用 RoundingMode enum(例如,RoundingMode.HALF_UP)的枚舉值辜御。
java.math.MathContext
- javase官方文檔描述
該對象是封裝上下文設(shè)置的不可變對象鸭你,它描述數(shù)字運(yùn)算符的某些規(guī)則,例如由 BigDecimal 類實(shí)現(xiàn)的規(guī)則擒权。
基本獨(dú)立設(shè)置為:
1袱巨、precision:某個(gè)操作使用的數(shù)字個(gè)數(shù);結(jié)果舍入到此精度
2碳抄、roundingMode:一個(gè) RoundingMode 對象愉老,該對象指定舍入使用的算法。
java.math.RoundingMode
- javase官方文檔描述
為可能丟棄精度的數(shù)值操作指定一種舍入行為剖效。每種舍入模式都指示如何計(jì)算返回舍入結(jié)果位數(shù)的最低有效位嫉入。如果返回的位數(shù)比表示精確數(shù)值結(jié)果所需的位數(shù)少,則舍棄的位數(shù)稱為舍棄部分璧尸,而不管這些位數(shù)對數(shù)值的作用如何咒林。換句話說,假設(shè)是一個(gè)數(shù)值爷光,舍棄部分的絕對值可能大于 1垫竞。
- 這是一種枚舉類型,以下是它的8種舍入方式的總結(jié):
- UP
遠(yuǎn)離零方向舍入的舍入模式蛀序。
始終對非零舍棄部分前面的數(shù)字加 1欢瞪。
注意,此舍入模式始終不會(huì)減少計(jì)算值的絕對值哼拔。
- DOWN
向零方向舍入的舍入模式引有。
從不對舍棄部分前面的數(shù)字加 1(即截尾)。
注意倦逐,此舍入模式始終不會(huì)增加計(jì)算值的絕對值譬正。
- CEILING
向正無限大方向舍入的舍入模式。
如果結(jié)果為正檬姥,則舍入行為類似于 RoundingMode.UP曾我;
如果結(jié)果為負(fù),則舍入行為類似于 RoundingMode.DOWN健民。
注意抒巢,此舍入模式始終不會(huì)減少計(jì)算值。
- FLOOR
向負(fù)無限大方向舍入的舍入模式秉犹。
如果結(jié)果為正蛉谜,則舍入行為類似于 RoundingMode.DOWN稚晚;
如果結(jié)果為負(fù),則舍入行為類似于 RoundingMode.UP型诚。
注意客燕,此舍入模式始終不會(huì)增加計(jì)算值。
- HALF_UP
向最接近數(shù)字方向舍入的舍入模式狰贯。
如果與兩個(gè)相鄰數(shù)字的距離相等也搓,則向上舍入。
如果被舍棄部分 >= 0.5涵紊,則舍入行為同 RoundingMode.UP傍妒;
否則舍入行為同 RoundingMode.DOWN。
注意摸柄,此舍入模式就是通常學(xué)校里講的四舍五入颤练。
- HALF_DOWN
向最接近數(shù)字方向舍入的舍入模式。
如果與兩個(gè)相鄰數(shù)字的距離相等塘幅,則向下舍入昔案。
如果被舍棄部分 > 0.5,則舍入行為同 RoundingMode.UP电媳;
否則舍入行為同 RoundingMode.DOWN踏揣。
- HALF_EVEN
向最接近數(shù)字方向舍入的舍入模式。
如果與兩個(gè)相鄰數(shù)字的距離相等匾乓,則向相鄰的偶數(shù)舍入捞稿。
如果舍棄部分左邊的數(shù)字為奇數(shù),則舍入行為同 RoundingMode.HALF_UP拼缝;
如果為偶數(shù)娱局,則舍入行為同 RoundingMode.HALF_DOWN。
注意咧七,在重復(fù)進(jìn)行一系列計(jì)算時(shí)衰齐,此舍入模式可以在統(tǒng)計(jì)上將累加錯(cuò)誤減到最小。此舍入模式也稱為“銀行家舍入法”继阻,主要在美國使用耻涛。此舍入模式類似于 Java 中對 float 和 double 算法使用的舍入策略。
- UNNECESSARY
用于斷言請求的操作具有精確結(jié)果的舍入模式瘟檩,因此不需要舍入抹缕。
如果對生成精確結(jié)果的操作指定此舍入模式,則拋出 ArithmeticException墨辛。
- 來個(gè)表格看一下吧(這是javase官方api的圖表)根據(jù)給定的舍入模式將輸入數(shù)字舍入為一位數(shù)的結(jié)果:
輸入數(shù)字 | UP | DOWN | CEILING | FLOOR | HALF_UP | HALF_DOWN | HALF_EVEN | UNNECESSARY |
---|---|---|---|---|---|---|---|---|
5.5 | 6 | 5 | 6 | 5 | 6 | 5 | 6 | 拋出 ArithmeticException |
2.5 | 3 | 2 | 3 | 2 | 3 | 2 | 2 | 拋出 ArithmeticException |
1.6 | 2 | 1 | 2 | 1 | 2 | 2 | 2 | 拋出 ArithmeticException |
1.1 | 2 | 1 | 2 | 1 | 1 | 1 | 1 | 拋出 ArithmeticException |
1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
-1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
-1.1 | -2 | -1 | -1 | -2 | -1 | -1 | -1 | 拋出 ArithmeticException |
-1.6 | -2 | -1 | -1 | -2 | -2 | -2 | -2 | 拋出 ArithmeticException |
-2.5 | -3 | -2 | -2 | -3 | -3 | -2 | -2 | 拋出 ArithmeticException |
-5.5 | -6 | -5 | -5 | -6 | -6 | -5 | -6 | 拋出 ArithmeticException |
注意:以上8中枚舉類型卓研,在使用中需要特別注意,尤其是除法運(yùn)算中的時(shí)候,需要制定枚舉類型奏赘,切記寥闪,切記。
2磨淌、使用環(huán)境
在進(jìn)行開發(fā)工作中橙垢,有時(shí)候會(huì)需要對一些數(shù)據(jù)進(jìn)行加、減伦糯、乘、除的運(yùn)算操作嗽元,這時(shí)候無論用int敛纲、float還是double進(jìn)行計(jì)算,都會(huì)存在精度丟失的風(fēng)險(xiǎn)剂癌,這時(shí)候我們需要借助一個(gè)對象BigDecimal 來幫助我們進(jìn)行計(jì)算淤翔。
BigDecimal 的構(gòu)造方法有許多個(gè),構(gòu)造參數(shù)可以傳int佩谷、float旁壮、double、String谐檀,但是經(jīng)實(shí)際應(yīng)用抡谐,使用構(gòu)造參數(shù)為float或double類型的構(gòu)造參數(shù)進(jìn)行計(jì)算的時(shí)候,轉(zhuǎn)換的BigDecimal 對象都會(huì)導(dǎo)致精度的丟失桐猬。通過實(shí)際驗(yàn)證麦撵,使用構(gòu)造參數(shù)為String的構(gòu)造方法可以避免精度丟失。
注意: 在以后需要進(jìn)行加溃肪、減免胃、乘、出計(jì)算的參數(shù)惫撰,都盡量用String來接收服務(wù)器返回的參數(shù)羔沙。
3、使用方式
以下是封裝的用于進(jìn)行計(jì)算的方法:
加法
/**
* 提供精確加法計(jì)算的add方法(例:1+2厨钻,1是被加數(shù)扼雏,2是加數(shù))
*
* @param value1 被加數(shù)
* @param value2 加數(shù)
* @return 兩個(gè)參數(shù)的和
*/
public static String add(String value1, String value2) {
BigDecimal b1 = new BigDecimal(value1);
BigDecimal b2 = new BigDecimal(value2);
return b1.add(b2).toString();
}
減法
/**
* 提供精確減法運(yùn)算的sub方法(例:1-2,1是被減數(shù),2是減數(shù))
*
* @param value1 被減數(shù)
* @param value2 減數(shù)
* @return 兩個(gè)參數(shù)的差
*/
public static String sub(String value1, String value2) {
BigDecimal b1 = new BigDecimal(value1);
BigDecimal b2 = new BigDecimal(value2);
return b1.subtract(b2).toString();
}
乘法
/**
* 提供精確乘法運(yùn)算的mul方法(例:1*2,1是被乘數(shù)莉撇,2是乘數(shù))
*
* @param value1 被乘數(shù)
* @param value2 乘數(shù)
* @return 兩個(gè)參數(shù)的積
*/
public static String mul(String value1, String value2) {
BigDecimal b1 = new BigDecimal(value1);
BigDecimal b2 = new BigDecimal(value2);
return b1.multiply(b2).toString();
}
除法
/**
* 提供精確的除法運(yùn)算方法div(例:1÷2呢蛤,1是被除數(shù),2是除數(shù))
*
* @param value1 被除數(shù)
* @param value2 除數(shù)
* @param scale 精確范圍
* @return 兩個(gè)參數(shù)的商
* @throws IllegalAccessException
*/
public static String div(String value1, String value2, int scale) throws IllegalAccessException {
//如果精確范圍小于0棍郎,拋出異常信息
if (scale < 0) {
throw new IllegalAccessException("精確度不能小于0");
}
BigDecimal b1 = new BigDecimal(value1);
BigDecimal b2 = new BigDecimal(value2);
//指定枚舉類型其障,可以指定RoundingMode的8種枚舉類型之一,對這8種枚舉類型如果有疑問,可以參考上面RoundingMode的總結(jié)和描述堂湖。
//這里指定的DOWN,對應(yīng)RoundingMode的ROUND_DOWN模式殴俱,會(huì)直接舍去精確度后面的數(shù)值汽抚。
return b1.divide(b2, scale, RoundingMode.DOWN).toString();
}
4抓狭、使用總結(jié)
用以上四個(gè)方法就可以應(yīng)付開發(fā)過程中最基礎(chǔ)的加、減造烁、乘否过、除的運(yùn)算了,但是在使用中要注意的情況:被減數(shù)和減數(shù)惭蟋、被除數(shù)和除數(shù)的位置不要顛倒了苗桂,否則就會(huì)和預(yù)期的計(jì)算結(jié)果相差十萬八千里了。