天天看點

Java BigDecimal和double

BigDecimal類

對于不需要任何準确計算精度的數字可以直接使用float或double,但是如果需要精确計算的結果,則必須使用BigDecimal類,而且使用BigDecimal類也可以進行大數的操作。BigDecimal類的常用方法如表11-15所示。

表11-15 BigDecimal類的常用方法

序号

方    法

類型

描    述

1

public BigDecimal(double val)

構造

将double表示形式轉換

為BigDecimal

2

public BigDecimal(int val)

将int表示形式轉換為

BigDecimal

3

public BigDecimal(String val)

将字元串表示

形式轉換為BigDecimal

4

public BigDecimal add(BigDecimal augend)

普通

加法

5

public BigDecimal subtract(BigDecimal

subtrahend)

減法

6

public BigDecimal multiply(BigDecimal

multiplicand)

乘法

7

public BigDecimal divide(BigDecimal

divisor)

除法

範例:進行四舍五入的四則運算

BigDecimal是Java中用來表示任意精确浮點數運算的類,在BigDecimal中,使用unscaledValue × 10-scale來表示一個浮點數。其中,unscaledValue是一個BigInteger,scale是一個int。從這個表示方法來看,BigDecimal隻能辨別有限小數,不過可以表示的資料範圍遠遠大于double,在實際應用中基本足夠了。

下面提一下兩個精度問題:

問題一:BigDecimal的精度問題

分析一下上面代碼的問題(注釋的内容表示此語句的輸出)

第一行:事實上,由于二進制無法精确地表示十進制小數0.1,但是編譯器讀到字元串"0.1"之後,必須把它轉成8個位元組的double值,是以,編譯器隻能用一個最接近的值來代替0.1了,即0.1000000000000000055511151231257827021181583404541015625。是以,在運作時,傳給BigDecimal構造函數的真正的數值是0.1000000000000000055511151231257827021181583404541015625。

第二行:BigDecimal能夠正确地把字元串轉化成真正精确的浮點數。

第三行:問題在于Double.toString會使用一定的精度來四舍五入double,然後再輸出。會。Double.toString(0.1000000000000000055511151231257827021181583404541015625)輸出的事實上是"0.1",是以生成的BigDecimal表示的數也是0.1。

第四行:基于前面的分析,事實上這一行代碼等價于第三行

結論:

1.如果你希望BigDecimal能夠精确地表示你希望的數值,那麼一定要使用字元串來表示小數,并傳遞給BigDecimal的構造函數。

2.如果你使用Double.toString來把double轉化字元串,然後調用BigDecimal(String),這個也是不靠譜的,它不一定按你的想法工作。

3.如果你不是很在乎是否完全精确地表示,并且使用了BigDecimal(double),那麼要注意double本身的特例,double的規範本身定義了幾個特殊的double值(Infinite,-Infinite,NaN),不要把這些值傳給BigDecimal,否則會抛出異常。

問題二:把double強制轉化成int,難道不是扔掉小數部分嗎?

原因還是在于二進制無法精确地表示某些十進制小數,是以1023.99999999999999在編譯之後的double值變成了1024。

是以,把double強制轉化成int确實是扔掉小數部分,但是你寫在代碼中的值,并不一定是編譯器生成的真正的double值。

驗證代碼:

前面提過BigDecimal可以精确地把double表示出來還記得吧。

我們也可以直接列印出d的二進制形式,根據IEEE 754的規定,我們可以算出0x4090000000000000=(1024)。