最近研究了一下Java的浮點數計算問題,從網路上查詢了相關的資料,彙總並經過了一些整理和調試,最後完成此文,歡迎大家指出其中的錯誤和問題。
在Java中,float宣告的變數是單精確度浮點數,double宣告的變數是雙精確度浮點數,顧名思義就是double型的實體佔用記憶體空間是float的兩倍。 float是4個位元組而double是8個位元組。 float和double類型的數據,無法精確表示計算結果,這是由於float和double是不精確的計算。大家可以透過下面程式碼可以看出來:
複製代碼代碼如下:
public class Test
{
public static void main(String[] args)
{
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.42);
System.out.println(4.015 * 100);
System.out.println(123.3 / 100);
}
}
運行的結果為:
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999
想要獲得理想的效果,我們可以嘗試使用java.text.DecimalFormat格式化浮點數:
DecimalFormat可以依照一定的格式格式化數字,常用的格式化字元是#、0等。例:
複製代碼代碼如下:
System.out.println(new java.text.DecimalFormat("0.00").format(3.125));
System.out.println(new java.text.DecimalFormat("0.00").format(3.135));
但是得到的結果是:
3.12
3.14
這是因為DecimalFormat是使用half-even 舍入(ROUND_HALF_EVEN),簡單的說就是向當四捨五入的5的時候向最近的偶數靠。所以使用DecimalForamt也無法得到可靠的浮點數。最後我們可以考慮使用BigDecimal來獲得更精確的計算:
BigDecimal提供了多個建構函數,和浮點數有關的有:
複製代碼代碼如下:
BigDecimal(double val) Translates a double into a BigDecimal.
BigDecimal(String val) Translates the String repre sentation of a BigDecimal into a BigDecimal.
但用double參數來建立物件得到不精確的值,只有透過String來建立物件才是最準確的。
例如:
複製代碼代碼如下:
BigDecimal bd1=new BigDecimal(0.05);
System.out.println(bd1.toString());
BigDecimal bd2=new BigDecimal("0.05");
System.out.println(bd2.toString());
得到結果:
0.05000000000000000277555756156289135105907917022705078125
0.05
所以,我們最後需要使用String來建立對象,這樣得到的結果才是最精確的。另外,如果是double數,我們還可以使用:BigDecimal.valueOf(double val),原因很簡單,其JDK原始碼如下所示:
複製代碼代碼如下:
public static BigDecimal valueOf(double val)
{
return new BigDecimal(Double.toString(val));
}
最後要說明的是:BigDecimal的加減乘除其實最終都回傳的是一個新的BigDecimal對象,因為BigDecimal是不可變的(immutable)的,在進行每一步運算時,都會產生一個新的對象,所以a .add(b);雖然做了加法操作,但是a並沒有保存加操作後的值,正確的用法應該是a=a.add(b)。