您好,登錄后才能下訂單哦!
本篇文章為大家展示了double類型計算精度丟失問題及解決方法,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
public class Test{ public static void main(String [] args){ System.out.println(0.06+0.01); System.out.println(1.0-0.42); System.out.println(4.015*100); System.out.println(303.1/1000); } }
輸出
0.06999999999999999 0.5800000000000001 401.49999999999994 0.30310000000000004
我們發現,計算出來的值和我們預期結果不一致。原因在于我們的計算機是二進制的。浮點數沒有辦法使用二進制進行精確表示。計算機的CPU表示浮點數由兩個部分組成:指數和尾數,這樣的表示方法一般都會失去一定的精確度,有些浮點數運算也會產生一定的誤差。如:2.4的二進制表示并非就是精確的2.4。反而最為接近的二進制表示是 2.3999999999999999。浮點數的值實際上是由一個特定的數學公式計算得到的。可參考http://blog.csdn.net/abing37/article/details/5332798
那么我們如何才能夠獲取我們想要的預期結果呢?特別是在處理金額交易計算上。其實java的float只能用來進行科學計算或工程計算,在大多數的商業計算中,一般采用java.math.BigDecimal類來進行精確計算。在使用BigDecimal類來進行計算的時候,主要分為以下步驟:
(1) 用float或者double變量構建BigDecimal對象。通常使用BigDecimal的構造方法或者靜態方法的valueOf()方法把基本類型的變量構建成BigDecimal對象。
(2) 通過調用BigDecimal的加,減,乘,除等相應的方法進行算術運算。
(3) 把BigDecimal對象轉換成float,double,int等類型。
BigDecimal類基本介紹
在修改實例之前,我們先簡單了解一下BigDecimal類的構造函數和成員方法。
BigDecimal(int var) //創建一個具有參數所指定整數值的對象。 BigDecimal(double var) //創建一個具有參數所指定雙精度值的對象。 BigDecimal(long var) //創建一個具有參數所指定長整數值的對象。 BigDecimal(String var) //創建一個具有參數所指定以字符串表示的數值的對象。
成員方法(BigDecimal 的運算方式 不支持 + - * / 這類的運算 它有自己的運算方法)
BigDecimal add(BigDecimal augend) //加法運算 BigDecimal subtract(BigDecimal subtrahend) //減法運算 BigDecimal multiply(BigDecimal multiplicand) //乘法運算 BigDecimal divide(BigDecimal divisor) //除法運算
好,既然我們知道方法了,那么我們就用新方法來解決一下上述的問題。修改一下代碼,如下:
import java.math.*; public class Test{ public static void main(String [] args){ double d1 = 0.06; double d2 = 0.01; BigDecimal b1 = new BigDecimal(d1); BigDecimal b2 = new BigDecimal(d2); System.out.println(b1.add(b2).doubleValue()); double d3 = 1.0; double d4 = 0.42; BigDecimal b3 = new BigDecimal(d3); BigDecimal b4 = new BigDecimal(d4); System.out.println(b3.subtract(b4).doubleValue()); double d5 = 4.015; double d6 = 100; BigDecimal b5 = new BigDecimal(d5); BigDecimal b6 = new BigDecimal(d6); System.out.println(b5.multiply(b6).doubleValue()); double d7 = 303.1; double d8 = 1000; BigDecimal b7 = new BigDecimal(d7); BigDecimal b8 = new BigDecimal(d8); System.out.println(b7.divide(b8).doubleValue()); } }
輸出
0.06999999999999999 0.5800000000000001 401.49999999999994 0.30310000000000004
我們發現結果還是不對。從上述實例我們知道調用的構造方法為BigDecimal(double var)。 BigDecimal(double val)將 double 轉換為 BigDecimal,后者是double的二進制浮點值準確的十進制表示形式。返回的BigDecimal的標度是使 (10scale × val) 為整數的最小值。這里也幾個特別要注意的地方:
(1)此構造方法的結果有一定的不可預知性。有人可能認為在 Java 中寫入 new BigDecimal(0.1) 所創建的 BigDecimal 正好等于 0.1(非標度值 1,其標度為 1),但是它實際上等于 0.1000000000000000055511151231257827021181583404541015625。這是因為 0.1 無法準確地表示為 double(或者說對于該情況,不能表示為任何有限長度的二進制小數)。這樣,傳入 到構造方法的值不會正好等于 0.1(雖然表面上等于該值)。
(2)另一方面,String 構造方法是完全可預知的:寫入 new BigDecimal("0.1") 將創建一個 BigDecimal,它正好 等于預期的 0.1。因此,比較而言,通常建議優先使用 String 構造方法。
(3)當 double 必須用作 BigDecimal 的源時,請注意,此構造方法提供了一個準確轉換;它不提供與以下操作相同的結果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 構造方法,將 double 轉換為 String。要獲取該結果,請使用 static valueOf(double) 方法。
根據上述描述,我們繼續修改下例子,修改后如下:
import java.math.*; public class Test{ public static void main(String [] args){ double d1 = 0.06; double d2 = 0.01; BigDecimal b1 = new BigDecimal(Double.toString(d1)); BigDecimal b2 = new BigDecimal(Double.toString(d2)); System.out.println(b1.add(b2).doubleValue()); double d3 = 1.0; double d4 = 0.42; BigDecimal b3 = new BigDecimal(Double.toString(d3)); BigDecimal b4 = new BigDecimal(Double.toString(d4)); System.out.println(b3.subtract(b4).doubleValue()); double d5 = 4.015; double d6 = 100; BigDecimal b5 = new BigDecimal(Double.toString(d5)); BigDecimal b6 = new BigDecimal(Double.toString(d6)); System.out.println(b5.multiply(b6).doubleValue()); double d7 = 303.1; double d8 = 1000; BigDecimal b7 = new BigDecimal(Double.toString(d7)); BigDecimal b8 = new BigDecimal(Double.toString(d8)); System.out.println(b7.divide(b8).doubleValue()); } }
輸出
0.07 0.58 401.5 0.3031
計算精度正確。
總結
(1)需要精確的表示兩位小數時我們需要把他們轉換為BigDecimal對象,然后再進行運算。
(2)使用BigDecimal(double val)構造函數時仍會存在精度丟失問題,建議使用BigDecimal(String val)。這就需要先把double類型(調用Double.toString(double var))轉換為字符串然后在作為BigDecimal(String val)構造函數的參數。轉換為BigDecimal對象之后再進行加減乘除操作,這樣精度就不會出現問題了。這也是為什么有關金錢數據存儲都使用BigDecimal。
上述內容就是double類型計算精度丟失問題及解決方法,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。