float和double類型的主要設計目的是爲了科學計算和工程計算。它們執行二進制浮點運算,這是爲了在廣域數值範圍上提供較爲精確的快速近似計算而精心設計的。然而,它們沒有提供徹底精確的結果,因此不該該被用於要求精確結果的場合。由於要讓一個float或者double精確地表示0.1(或者10的任何負數次方值)是不可能的,float和double類型對於貨幣計算尤其不合適,由於要讓一個float或double精確地表示0.1或10的任何其餘負數次方值是不可能的。好比System.out.println(2.0-1.1)將會打印0.899999999999999,而不是你所但願的0.9,這種舍入錯誤產生的緣由是浮點數其實是用二進制系統實現的,而分數1/10在二進制系統中沒有精確的表示,其道理就如同在十進制系統中沒法精確表示1/3同樣;再好比0.5在二進制系統中有精確表示,而0.55在二進制系統中沒有精確表示。 java
System.out.println(1.03 – .42); System.out.println(1.00 – 9 * .10);
輸出結果爲:
0.6100000000000001
0.09999999999999998 sql
你可能會認爲,只要在打印以前將結果作一下舍入就能夠解決這個問題,但遺憾的是,這種作法並不老是可行。
例如:假設你口袋裏有1美圓,你看到貨架上有一排糖果,標價分別是10美分、20美分、30美分等等,一直到1美圓。你打算從標價爲10美分的開始買,每種買一顆,直到不能支付貨架上的任何一種價格爲止,那麼你能夠買多少顆糖果?還會找回多少零頭?下面是一個簡單的程序,用來解決這個問題: 性能
double funds = 1.00; int itemsBought = 0; for (double price = .10; funds >= price; price += .10) { funds -= price; itemsBought++; } System.out.println(itemsBought + " items bought."); System.out.println("Change: $" + funds);
若是運行這個程序你會發現你能夠支付3顆糖果,而且還剩0.3999999999999999美圓。顯然這個答案是不正確的。解決這個問題的正確辦法是使用BigDecimal、int或long進行貨幣計算。 spa
下面看看使用BigDecimal類型代替double的程序: 設計
final BigDecimal TEN_CENTS = new BigDecimal(".10"); int itemsBought = 0; BigDecimal funds = new BigDecimal("1.00"); for (BigDecimal price = TEN_CENTS; funds.compareTo(price) >= 0; price = price .add(TEN_CENTS)) { itemsBought++; funds = funds.subtract(price); } System.out.println(itemsBought + " items bought."); System.out.println("Money left over: $" + funds);
輸出結果0.00和4 這樣就正確了
code
關於BigDecimal的方法咱們下去再仔細看看;
這裏要說的是BigDecimal是引用數據類型 ci
總而言之: 對於任何要精確答案的計算任務,請不要用double和float。若是你想 計算十進制小數點,而且不介意非基本類型帶來的不便,那就用BigDecimal;使用 BigDecimal還有個額外的好處就是他容許你徹底控制舍入,每當一個操做涉及舍入的時候 它容許你從8種舍入模式中選擇一種。若是你正確經過法定要求的舍入行爲進行業務計算使用BigDecimal時很是方便的;若是性能十分關鍵,而且你又不介意本身記錄十進制的 小數點,並且涉及數值不大,就可使用int或者long; 若是數值範圍沒有超過9位的十進制數字,能夠用int;若是數值範圍沒有超過18位的十進制數字,用long 若是範圍超過了18位數,那咱們必須使用BigDecimal。 it