二進制運算誤差問題


我們知道在處理一些浮點數的時候,運算會產生誤差,得到的是類似無限循環。

例子:

  

    @Test
    public void Demo(){
        double a1=2.0;
        double a2=1.1;
        System.out.println(a2-a1);//-0.8999999999999999
    }

其原因是計算機所使用二進制01代碼無法准確表示某些帶小數位的十進制數據。

 

下面我們來分析下:
我們知道將一個十進制數值轉換為二進制數值,需要通過下面的計算方法:
1. 整數部分:連續用該整數除以2,取余數,然后商再除以2,直到商等於0為止。然后把得到的各個余數按相反的順序排列。簡稱"除2取余法"。
2. 小數部分:十進制小數轉換為二進制小數,采用"乘2取整,順序排列"法。用2乘以十進制小數,將得到的整數部分取出,再用2乘余下的小數部分,然后再將積的整數部分取出,如此進行,直到積中的小數部分為0或者達到所要求的精度為止。然后把取出的整數部分按順序排列起來,即先取出的整數部分作為二進制小數的高位,后取出的整數部分作為低位有效位。簡稱"乘2取整法"。
3. 含有小數的十進制數轉換成二進制,整數、小數部分分別進行轉換,然后相加。

例如:將十進制數值25.75轉換為二進制數值,步驟如下:

25(整數部分)
25/2=12......1
12/2=6.......0
6/2=3......0
3/2=1......1
1/2=0......1
(25) 10=(11001) 2

0.75(小數部分)
0.75*2=1.5......1
0.5*2=1......1
(0.75) 10=(0.11) 2
(25.75) 10=(11001) 2+(0.11) 2=(11001.11) 2

按照上述方法,我們將0.65及0.6轉換為二進制代碼:
(0.65)10 = (0.101001100110011001100110011001100110011......)2
(0.6) 10 = (0.10011001100110011001100110011001100110011......)2

后面的省略號表示已經算不完了,后面在無限重復 0011 這段二進制數值。

 

如何解決這個問題?知道其根本原因后,我們知道是無法從根本上解決這個問題的,但我們可以有一些曲線救國的方法,下面列舉幾個:
1. 因為二進制數值可以准確表示整數(可以使用整數轉換為二進制方法驗證下),所以可以將小數乘以10或100等變成整數,然后做整數運算,最后再通過除以10或100等獲得結果;
2. 通過截取結果的有效小數位數等,來取得最好的近似結果,然后在做處理。
3. 對於可以用有限長度的二進制數值表示的十進制數值,可以使用存儲位數大於其長度的數據類型。

 

java中提供了BigDecimal這個超大的類,用來存儲浮點型,使用它可以處理二進制誤差問題。

@Test
    public void Demo1(){
        BigDecimal b1=new BigDecimal(2.0+"");//注意:只能使用String類型的構造
        System.out.println(b1.subtract(new BigDecimal(1.1+"")));//0.9
    }

 

其他具體方法查看api。大的整形數據使用BigDecimal

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM