日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

[ JAVA编程 ] double类型计算精度丢失问题及解决方法

發(fā)布時間:2024/5/24 综合教程 33 生活家
生活随笔 收集整理的這篇文章主要介紹了 [ JAVA编程 ] double类型计算精度丢失问题及解决方法 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

如果你在測試金融相關(guān)產(chǎn)品,請務(wù)必覆蓋交易金額為小數(shù)的場景。特別是使用Java語言的初級開發(fā)。

Java基本實例

先來看Java中double類型數(shù)值加、減、乘、除計算式實例:

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);    
    }
}

運行結(jié)果如下:

D:linyfeng>java Test
0.06+0.01 = 0.06999999999999999
1.0-0.42 = 0.5800000000000001
4.015*100 = 401.49999999999994
303.1/1000 = 0.30310000000000004

我們發(fā)現(xiàn),計算出來的值和我們預期結(jié)果不一致。原因在于我們的計算機是二進制的。浮點數(shù)沒有辦法使用二進制進行精確表示。計算機的CPU表示浮點數(shù)由兩個部分組成:指數(shù)和尾數(shù),這樣的表示方法一般都會失去一定的精確度,有些浮點數(shù)運算也會產(chǎn)生一定的誤差。如:2.4的二進制表示并非就是精確的2.4。反而最為接近的二進制表示是 2.3999999999999999。浮點數(shù)的值實際上是由一個特定的數(shù)學公式計算得到的。可參考http://blog.csdn.net/abing37/article/details/5332798

那么我們?nèi)绾尾拍軌颢@取我們想要的預期結(jié)果呢?特別是在處理金額交易計算上。其實java的float只能用來進行科學計算或工程計算,在大多數(shù)的商業(yè)計算中,一般采用java.math.BigDecimal類來進行精確計算。在使用BigDecimal類來進行計算的時候,主要分為以下步驟:

(1)用float或者double變量構(gòu)建BigDecimal對象。通常使用BigDecimal的構(gòu)造方法或者靜態(tài)方法的valueOf()方法把基本類型的變量構(gòu)建成BigDecimal對象。

(2)通過調(diào)用BigDecimal的加,減,乘,除等相應的方法進行算術(shù)運算。

(3)把BigDecimal對象轉(zhuǎn)換成float,double,int等類型。

BigDecimal類基本介紹

在修改實例之前,我們先簡單了解一下BigDecimal類的構(gòu)造函數(shù)和成員方法。

BigDecimal(int var)  //創(chuàng)建一個具有參數(shù)所指定整數(shù)值的對象。
BigDecimal(double var) //創(chuàng)建一個具有參數(shù)所指定雙精度值的對象。
BigDecimal(long var)  //創(chuàng)建一個具有參數(shù)所指定長整數(shù)值的對象。
BigDecimal(String var) //創(chuàng)建一個具有參數(shù)所指定以字符串表示的數(shù)值的對象。

成員方法(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());    
    }
}

運行結(jié)果:

D:linyfeng>java Test
0.06999999999999999
0.5800000000000001
401.49999999999994
0.30310000000000004

我們發(fā)現(xiàn)結(jié)果還是不對。從上述實例我們知道調(diào)用的構(gòu)造方法為BigDecimal(double var)。BigDecimal(double val)將 double 轉(zhuǎn)換為 BigDecimal,后者是double的二進制浮點值準確的十進制表示形式。返回的BigDecimal的標度是使 (10scale × val) 為整數(shù)的最小值。這里也幾個特別要注意的地方:

(1)此構(gòu)造方法的結(jié)果有一定的不可預知性。有人可能認為在 Java 中寫入 new BigDecimal(0.1) 所創(chuàng)建的 BigDecimal 正好等于 0.1(非標度值 1,其標度為 1),但是它實際上等于 0.1000000000000000055511151231257827021181583404541015625。這是因為 0.1 無法準確地表示為 double(或者說對于該情況,不能表示為任何有限長度的二進制小數(shù))。這樣,傳入 到構(gòu)造方法的值不會正好等于 0.1(雖然表面上等于該值)。
(2)另一方面,String 構(gòu)造方法是完全可預知的:寫入 new BigDecimal("0.1") 將創(chuàng)建一個 BigDecimal,它正好 等于預期的 0.1。因此,比較而言,通常建議優(yōu)先使用 String 構(gòu)造方法。
(3)當 double 必須用作 BigDecimal 的源時,請注意,此構(gòu)造方法提供了一個準確轉(zhuǎn)換;它不提供與以下操作相同的結(jié)果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 構(gòu)造方法,將 double 轉(zhuǎn)換為 String。要獲取該結(jié)果,請使用 static valueOf(double) 方法。

根據(jù)上述描述,我們繼續(xù)修改下例子,修改后如下:

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());    
    }
}

運行結(jié)果如下:

D:linyfeng>java Test
0.07
0.58
401.5
0.3031

計算精度正確。

總結(jié)

(1)需要精確的表示兩位小數(shù)時我們需要把他們轉(zhuǎn)換為BigDecimal對象,然后再進行運算。
(2)使用BigDecimal(double val)構(gòu)造函數(shù)時仍會存在精度丟失問題,建議使用BigDecimal(String val)。這就需要先把double類型(調(diào)用Double.toString(double var))轉(zhuǎn)換為字符串然后在作為BigDecimal(String val)構(gòu)造函數(shù)的參數(shù)。轉(zhuǎn)換為BigDecimal對象之后再進行加減乘除操作,這樣精度就不會出現(xiàn)問題了。這也是為什么有關(guān)金錢數(shù)據(jù)存儲都使用BigDecimal。

參考文檔

1、使用BigDecimal進行精確運算

2、java中double和float精度丟失問題及解決方法

3、百度百科BigDecimal

總結(jié)

以上是生活随笔為你收集整理的[ JAVA编程 ] double类型计算精度丢失问题及解决方法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。