1. 程式人生 > >JAVA精確計算

JAVA精確計算

double型資料不能進行精確計算

JAVA的double型資料不能進行精確計算,許多程式語言也是一樣。請看下面的例子:

從這個例子可以看出計算的結果出現了誤差,所以double型資料進行金額計算是不可靠的。

 四捨五入**(截斷)

上面這個例子中,如果把計算結果按照指定的位數進行四捨五入,似乎就是我們想要的結果,但是在數學計算中四捨五入意味著誤差,並且JDK中沒有提供保留指定位數的四捨五入的API。

有一種方式是使用java.text.DecimalFormat類,它可以把數字格式化為指定的格式,但是它的舍入模式並不是我們數學上的四捨五入。請看下面的例子:

    從這個例子可以看到,DecimalFormat類把1.145保留兩位小數之後是1.14而不是1.15,所以不能

DecimalFormat類對double型資料進行四捨五入。

科學記數法

double型數值在整數部分超過7時就自動轉化為科學記數法表示,請看下面的例子:

    通常情況下,我們不需要用科學記數法表示,從科學記數法轉回普通的記數方法也是比較繁瑣的。

BigDecimal是一個不變的、任意精度的有符號的十進位制數物件。它提供了四個構造方法,其中有兩個是用BigInteger構造的,在這裡重點講解用double和String構造的構造方法。

Constructor Summary

BigDecimal(double val)           Translates a double into a BigDecimal.

BigDecimal(String val)           Translates the String representation of a BigDecimal into a BigDecimal.

BigDecimal(double)是把一個double型十進位制數構造為一個BigDecimal例項。

BigDecimal(String)是把一個以String表示的十進位制數構造為BigDecimal例項。

通常情況下,對於浮點數我們都會定義為double型別,但是在JDK的API文件中有這樣一段描述:BigDecimal(double)這個構造方法可能會有不可預知的結果。有人可能設想new BigDecimal(0.1)

等於0.1,但它實際上是等於0.1000000000000000055511151231257827021181583404541015625,所以0.1不能用一個double型變數精確表示。因此,0.1被放進構造方法之後並不精確等於0.1,儘管外觀看起來是相等的。

然而(String)構造方法則是完全可預知的new BigDecimal(“0.1”)精確的等於0.1,因此,new BigDecimalString)構造方法是被優先推薦使用的。

    請看下面的例子:

    從這個例子可以看出,要進行精確的計算,必須使用BigDecimal(String val)

用BigDecimal可以進行精確的四則運算,請看下面三個例子:

上面三個例子演示了用BigDecimal進行加法、減法、乘法運算,進行計算的資料與本文第一個例子是一樣的,而計算的結果都是正確的。用BigDecimal進行除法運算時涉及到保留有效位數和舍入模式問題,這也是BigDecimal類使用的重點,在下面的章節介紹。

舍入模式

BigDecimal定義了以下舍入模式,只有在做除法運算四捨五入時才會用到舍入模式。下面的各小節的例子是應用各種舍入模式把double數值保留小數點後兩位:

Field Summary

static int

ROUND_CEILING           Rounding mode to round towards positive infinity.

static int

ROUND_DOWN           Rounding mode to round towards zero.

static int

ROUND_FLOOR           Rounding mode to round towards negative infinity.

static int

ROUND_HALF_DOWN           Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.

static int

ROUND_HALF_EVEN           Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.

static int

ROUND_HALF_UP           Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.

static int

ROUND_UNNECESSARY           Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.

static int

ROUND_UP           Rounding mode to round away from zero.

ROUND_CEILING模式是向正無窮大方向舍入。請看下面的例子:

   在數軸上,對於正數,如果數值與1.02之間的距離不到0.01就進位到1.02,所以除非數值等於1.01不用進位,否則所有大於1.01且小於1.02的數值都要進位到1.02,因為在數軸上1.02比1.01更靠近正無窮大;對於負數,所有小於等於-1.01且大於-1.02的數值都要捨棄從小數點後第三位開始的所有數字,因為在數軸上-1.01比-1.02更靠近正無窮大,這就是ROUND_CEILING模式——向正無窮大方向舍入。

ROUND_FLOOR模式是向負無窮大方向舍入。請看下面的例子:

在數軸上,1.01比1.02更靠近負無窮大,所以,所有大於等於1.01且小於1.02的數值都要捨棄從小數點後第三位開始的所有數字;-1.02比-1.01更靠近負無窮大,所以,所有大於等於-1.02且小於-1.01的數值都要捨棄從小數點後第三位開始的所有數字,這就是ROUND_FLOOR模式——向靠近負無窮大的方向舍入。

ROUND_DOWN模式是向靠近零的方向舍入。請看下面的例子:

    在數軸上,1.01比1.02更靠近零,所以,所有大於等於1.01且小於1.02的數值都要捨棄從小數點後第三位開始的所有數字;-1.01比-1.02更靠近零,所以,所有大於-1.02且小於等於-1.01的數值都要捨棄從小數點後第三位開始的所有數字,這就是ROUND_DOWN模式——向靠近零的方向舍入。

ROUND_UP模式是向遠離零的方向舍入。請看下面的例子:

    ROUND_UP模式實際上對於正數就是向正無窮大方向舍入ROUND_CEILING,對於負數就是向負無窮大方向舍入ROUND_FLOOR,這就是ROUND_UP模式——向遠離零的方向舍入。

ROUND_ UNNECESSARY模式是不使用舍入模式。如果可以確保計算結果是精確的,則可以指定此模式,否則如果指定了使用此模式卻遇到了不精確的計算結果,則丟擲ArithmeticException。請看下面的例子:

    在不使用舍入模式的情況下,1.01和1.01000都可以精確的保留到小數點後第二位,而1.01001卻不能,所以程式丟擲了異常。

ROUND_HALF_DOWN模式是向距離最近的一邊舍入,如果兩邊距離相等,就向靠近零的方向舍入。請看下面的例子:

在數軸上,1.01501與1.02之間的距離比1.01501與1.01之間的距離近,所以1.01501進位到1.02;1.01500和1.01之間的距離與1.01500和1.02之間的距離相等,而1.01比1.02更靠近零,所以1.01500保留兩位小數為1.01,這就是ROUND_HALF_DOWN模式——向距離最近的一邊舍入,如果兩邊距離相等,就向靠近零的方向舍入。

前面章節提到的java.text.DecimalFormat類使用的就是ROUND_HALF_DOWN模式。

(日常計算中使用)ROUND_HALF_UP

ROUND_HALF_UP模式是向距離最近的一邊舍入,如果兩邊距離相等,就向遠離零的方向舍入。請看下面的例子:

解釋:一個數1.015如保留兩2位小數,則舍入後是1.01或1.02,ROUND_HALF_UP此模式為向距離1.01和1.02近的方向舍;如果距離一樣向0方向舍。便為1.02 。

在數軸上,1.01501與1.02之間的距離比1.01501與1.01之間的距離近,所以1.01501進位到1.02;1.01500和1.01之間的距離與1.01500和1.02之間的距離相等,而1.02比1.01更遠離零,所以1.01500保留兩位小數為1.02,這就是ROUND_HALF_UP模式——向距離最近的一邊舍入,如果兩邊距離相等,就向遠離零的方向舍入。

從例子中可以看出:ROUND_HALF_UP模式就是我們數學上的四捨五入。

ROUND_HALF_UP模式是向距離最近的一邊舍入,如果兩邊距離相等且小數點後保留的位數是奇數,就使用ROUND_HALF_UP模式;如果兩邊距離相等且小數點後保留的位數是偶數,就使用ROUND_HALF_DOWN模式。請看下面的例子:

    在以上8種舍入模式中,我們日常計算只需要使用ROUND_HALF_UP模式。

    從結果可以看出,雖然指定了保留小數點後10位,但由於151.3除以100的商可以精確到小數點後3位,所以b3的值就是1.513。