1.引言
float和double類型的主要設(shè)計目標是為了科學計算和工程計算屎蜓。他們執(zhí)行二進制浮點運算粉洼,這是為了在廣域數(shù)值范圍上提供較為精確的快速近似計算而精心設(shè)計的刀崖。然而颖榜,它們沒有提供完全精確的結(jié)果棚饵,所以不應(yīng)該被用于要求精確結(jié)果的場合煤裙。但是,商業(yè)計算往往要求結(jié)果精確蟹地,這時候BigDecimal就派上大用場啦积暖。
先看下面代碼:
public static void main(String[] args)
{
System.out.println(0.2 + 0.1);
System.out.println(0.3 - 0.1);
System.out.println(0.2 * 0.1);
System.out.println(0.3 / 0.1);
}
運行結(jié)果如下:
你認為你看錯了,但結(jié)果卻是是這樣的怪与。問題在哪里呢?原因在于我們的計算機是二進制的缅疟。浮點數(shù)沒有辦法是用二進制進行精確表示分别。我們的CPU表示浮點數(shù)由兩個部分組成:指數(shù)和尾數(shù),這樣的表示方法一般都會失去一定的精確度存淫,有些浮點數(shù)運算也會產(chǎn)生一定的誤差耘斩。如:2.4的二進制表示并非就是精確的2.4。反而最為接近的二進制表示是 2.3999999999999999桅咆。浮點數(shù)的值實際上是由一個特定的數(shù)學公式計算得到的括授。
其實java的float只能用來進行科學計算或工程計算,在大多數(shù)的商業(yè)計算中岩饼,一般采用java.math.BigDecimal類來進行精確計算荚虚。
2.BigDecimal構(gòu)造方法
1.public BigDecimal(double val) 將double表示形式轉(zhuǎn)換為BigDecimal 不建議使用這個構(gòu)造函數(shù)
2.public BigDecimal(int val) 將int表示形式轉(zhuǎn)換成BigDecimal
3.public BigDecimal(String val) 將String表示形式轉(zhuǎn)換成BigDecimal
為什么不建議采用第一種構(gòu)造方法呢?來看例子
{
BigDecimal bigDecimal = new BigDecimal(2);
BigDecimal bDouble = new BigDecimal(2.3);
BigDecimal bString = new BigDecimal("2.3");
System.out.println("bigDecimal=" + bigDecimal);
System.out.println("bDouble=" + bDouble);
System.out.println("bString=" + bString);
}
運行結(jié)果如下:
為什么會出現(xiàn)這種情況呢籍茧?
JDK的描述:
1版述、參數(shù)類型為double的構(gòu)造方法的結(jié)果有一定的不可預(yù)知性。有人可能認為在Java中寫入newBigDecimal(0.1)所創(chuàng)建的BigDecimal正好等于 0.1(非標度值 1寞冯,其標度為 1)渴析,但是它實際上等于0.1000000000000000055511151231257827021181583404541015625。這是因為0.1無法準確地表示為 double(或者說對于該情況吮龄,不能表示為任何有限長度的二進制小數(shù))俭茧。這樣,傳入到構(gòu)造方法的值不會正好等于 0.1(雖然表面上等于該值)漓帚。
2母债、 另一方面,String 構(gòu)造方法是完全可預(yù)知的:寫入 newBigDecimal("0.1") 將創(chuàng)建一個 BigDecimal胰默,它正好等于預(yù)期的 0.1场斑。因此,比較而言牵署,通常建議優(yōu)先使用String構(gòu)造方法漏隐。
當double必須用作BigDecimal的源時,請使用Double.toString(double)轉(zhuǎn)成String奴迅,然后使用String構(gòu)造方法青责,或使用BigDecimal的靜態(tài)方法valueOf挺据,如下
public static void main(String[] args)
{
BigDecimal bDouble1 = BigDecimal.valueOf(2.3);
BigDecimal bDouble2 = new BigDecimal(Double.toString(2.3));
System.out.println("bDouble1=" + bDouble1);
System.out.println("bDouble2=" + bDouble2);
}
結(jié)果:3.BigDecimal加減乘除運算
對于常用的加,減脖隶,乘扁耐,除,BigDecimal類提供了相應(yīng)的成員方法产阱。
public BigDecimal subtract(BigDecimal value); //減法
public BigDecimal multiply(BigDecimal value); //乘法
public BigDecimal divide(BigDecimal value); //除法
大概的用法如下
public static void main(String[] args)
{
BigDecimal a = new BigDecimal("4.5");
BigDecimal b = new BigDecimal("1.5");
System.out.println("a + b =" + a.add(b));
System.out.println("a - b =" + a.subtract(b));
System.out.println("a * b =" + a.multiply(b));
System.out.println("a / b =" + a.divide(b));
}
運行結(jié)果這里有一點需要注意的是除法運算divide.
BigDecimal除法可能出現(xiàn)不能整除的情況婉称,比如 4.5/1.3,這時會報錯java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
其實divide方法有可以傳三個參數(shù)
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
第一參數(shù)表示除數(shù)构蹬, 二個參數(shù)表示小數(shù)點后保留位數(shù)王暗,第三個參數(shù)表示舍入模式。
只有在作除法運算或四舍五入時才用到舍入模式庄敛,有下面這幾種:
ROUND_UP :向遠離零的方向舍入俗壹。舍棄非零部分,并將非零舍棄部分相鄰的一位數(shù)字加一藻烤。
ROUND_DOWN :向接近零的方向舍入绷雏。舍棄非零部分,同時不會非零舍棄部分相鄰的一位數(shù)字加一怖亭,采取截取行為涎显。
ROUND_CEILING :向正無窮的方向舍入。如果為正數(shù)依许,舍入結(jié)果同ROUND_UP一致棺禾;如果為負數(shù),舍入結(jié)果同ROUND_DOWN一致峭跳。注意:此模式不會減少數(shù)值大小膘婶。
ROUND_FLOOR :向負無窮的方向舍入。如果為正數(shù)蛀醉,舍入結(jié)果同
ROUND_DOWN一致悬襟;如果為負數(shù),舍入結(jié)果同ROUND_UP一致拯刁。注意:此模式不會增加數(shù)值大小脊岳。
ROUND_HALF_UP :向“最接近”的數(shù)字舍入,如果與兩個相鄰數(shù)字的距離相等垛玻,則為向上舍入的舍入模式割捅。如果舍棄部分>= 0.5,則舍入行為與ROUND_UP相同帚桩;否則舍入行為與ROUND_DOWN相同亿驾。這種模式也就是我們常說的我們的“四舍五入”。
ROUND_HALF_DOWN :向“最接近”的數(shù)字舍入账嚎,如果與兩個相鄰數(shù)字的距離相等莫瞬,則為向下舍入的舍入模式儡蔓。如果舍棄部分> 0.5,則舍入行為與ROUND_UP相同疼邀;否則舍入行為與ROUND_DOWN相同喂江。這種模式也就是我們常說的我們的“五舍六入”。
ROUND_HALF_EVEN :向“最接近”的數(shù)字舍入旁振,如果與兩個相鄰數(shù)字的距離相等获询,則相鄰的偶數(shù)舍入。如果舍棄部分左邊的數(shù)字奇數(shù)拐袜,則舍入行為與 ROUND_HALF_UP 相同筐付;如果為偶數(shù),則舍入行為與 ROUND_HALF_DOWN 相同阻肿。注意:在重復(fù)進行一系列計算時,此舍入模式可以將累加錯誤減到最小沮尿。此舍入模式也稱為“銀行家舍入法”丛塌,主要在美國使用。四舍六入畜疾,五分兩種情況赴邻,如果前一位為奇數(shù),則入位啡捶,否則舍去姥敛。
ROUND_UNNECESSARY :斷言請求的操作具有精確的結(jié)果,因此不需要舍入瞎暑。如果對獲得精確結(jié)果的操作指定此舍入模式彤敛,則拋出ArithmeticException。
按照各自的需要了赌,可傳入合適的第三個參數(shù)墨榄。四舍五入采用 ROUND_HALF_UP
需要對BigDecimal進行截斷和四舍五入可用setScale方法,例:
更多請參考:https://www.cnblogs.com/Jason-Xiang/p/10220231.html