程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> JAVA浮點數計算精度損失底層原理與處理方案

JAVA浮點數計算精度損失底層原理與處理方案

編輯:關於JAVA

JAVA浮點數計算精度損失底層原理與處理方案。本站提示廣大學習愛好者:(JAVA浮點數計算精度損失底層原理與處理方案)文章只能為提供參考,不一定能成為您想要的結果。以下是JAVA浮點數計算精度損失底層原理與處理方案正文


  浮點數會有精度損失這個在上大學的時分就曾經原告知,但是至今完全沒有想明白其中的原由,教師講的時分也是一筆帶過的,自己也沒有好好揣摩。終於在任務的時分碰到了,於是google了一番。

問題:

  對兩個double類型的值停止運算,有時會呈現後果值異常的問題。比方: 

1     System.out.println(19.99+20);
2     System.out.println(1.0-0.66);
3     System.out.println(0.033*100);
4     System.out.println(12.3/100);

輸入:

39.989999999999995
0.33999999999999997
3.3000000000000003
0.12300000000000001

  Java中的復雜浮點數類型float和double不可以准確運算。這個問題其實不是JAVA的bug,由於計算機自身是二進制的,而浮點數實踐上只是個近似值,所以從二進制轉化為十進制浮點數時,精度容易喪失,招致精度下降。

關於精度損失的原理可以很復雜的講,首先一個正整數在計算機中表示運用01010方式表示的,浮點數也不例外。

  比方11,11除以2等於5余1

 

       5除以2等於2余1

 

       2除以2等於1余0

 

       1除以2等於0余1

 

  所以11二進制表示為:1011.

 

  double類型占8個字節,64位,第1位為符號位,前面11位是指數局部,剩余局部是無效數字。

 

  正整數除以2一定會有個止境的,之後二進制復原成十進制只需求乘以2即可。

 

  舉個例子:0.99用的無效數字局部,

 

       0.99 * 2 = 1+0.98 --> 1

 

       0.98 * 2 = 1+0.96 --> 1

 

       0.96 * 2 = 1+0.92 -- >1

 

       0.92 * 2 = 1+0.84 -- >1

 

         ...............

 

  這樣循環往復是沒法有止境的,而double無效數字無限,所以肯定會有損失,所以二進制無法精確表示0.99,好像十進制無法精確表示1/3一樣。

處理方法:

  在《Effective Java》中提到一個准繩,那就是float和double只能用來作迷信計算或許是工程計算,但在商業計算中我們要用java.math.BigDecimal,經過運用BigDecimal類可以處理上述問題,首先需求留意的是,直接運用字符串來結構BigDecimal是相對沒有精度損失的,假如用double或許把double轉化成string來結構BigDecimal仍然會有精度損失,所以我覺得這種處理辦法就是在運用中就把浮點數用string來表示寄存,觸及到運算直接用string結構double,否則一定會有精度損失。

1. 相加

 1 /**
 2  * 相加
 3  * @param double1
 4  * @param double2
 5  * @return
 6  */
 7 public static double add(String doubleValA, String doubleValB) {  
 8     BigDecimal a2 = new BigDecimal(doubleValA);  
 9     BigDecimal b2 = new BigDecimal(doubleValB);  
10     return a2.add(b2).doubleValue();  
11 }

2. 相減

 1 /**
 2  * 相減
 3  * @param double1
 4  * @param double2
 5  * @return
 6  */
 7 public static double sub(String doubleValA, String doubleValB) {  
 8     BigDecimal a2 = new BigDecimal(doubleValA);  
 9     BigDecimal b2 = new BigDecimal(doubleValB);  
10     return a2.subtract(b2).doubleValue();
11 }

3. 相乘

 1 /**
 2  * 相乘
 3  * @param double1
 4  * @param double2
 5  * @return
 6  */
 7 public static double mul(String doubleValA, String doubleValB) {  
 8     BigDecimal a2 = new BigDecimal(doubleValA);  
 9     BigDecimal b2 = new BigDecimal(doubleValB);  
10     return a2.multiply(b2).doubleValue();
11 }

4. 相除

 1 /**
 2  * 相除
 3  * @param double1
 4  * @param double2
 5  * @param scale 除不盡時指定精度
 6  * @return
 7  */
 8 public static double div(String doubleValA, String doubleValB, int scale) {  
 9     BigDecimal a2 = new BigDecimal(doubleValA);  
10     BigDecimal b2 = new BigDecimal(doubleValB);
11     return a2.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();  
12 }

5. 主函數調用

1 public static void main(String[] args) {
2     String doubleValA = "3.14159267";
3     String doubleValB = "2.358";
4     System.out.println("add:" + add(doubleValA, doubleValB));
5     System.out.println("sub:" + sub(doubleValA, doubleValB));
6     System.out.println("mul:" + mul(doubleValA, doubleValB));
7     System.out.println("div:" + div(doubleValA, doubleValB, 8));
8 }

後果展現如下所示:

  add:5.49959267
  sub:0.78359267
  mul:7.40787551586
  div:1.33231241

所以最好的辦法是完全丟棄double,用string和java.math.BigDecimal。

  java遵照IEEE制定的浮點數表示法來停止float,double運算。這種構造是一種迷信計數法,用符號、指數和尾數來表示,底數定為2——即把一個浮點數表示為尾數乘以2的指數次方再添上符號。詳細底層如何存儲以及如何停止運轉請持續關注我的博客,後續我會將概況總結好的。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved