1. 程式人生 > >java double相加精度誤差

java double相加精度誤差

問題的提出:   
  編譯執行下面這個程式會看到什麼?   
  public   class   Test{   
          public   static   void   main(String   args[]){   
                  System.out.println(0.05+0.01);   
                  System.out.println(1.0-0.42);   
                  System.out.println(4.015*100);   
                  System.out.println(123.3/100);   

          }   
  };   

  你沒有看錯!結果確實是   
  0.060000000000000005   
  0.5800000000000001   
  401.49999999999994   
  1.2329999999999999   

  Java中的簡單浮點數型別float和double不能夠進行運算。不光是Java,在其它很多程式語言中也有這樣的問題。在大多數情況下,計算的結果是準確的,但是多試幾次(可以做一個迴圈)就可以試出類似上面的錯誤。現在終於理解為什麼要有BCD碼了。   
  這個問題相當嚴重,如果你有9.999999999999元,你的計算機是不會認為你可以購買10元的商品的。   
  在有的程式語言中提供了專門的貨幣型別來處理這種情況,但是Java沒有。現在讓我們看看如何解決這個問題。   




  四捨五入   
  我們的第一個反應是做四捨五入。Math類中的round方法不能設定保留幾位小數,我們只能象這樣(保留兩位):   
  public   double   round(double   value){   
          return   Math.round(value*100)/100.0;   
  }   

  非常不幸,上面的程式碼並不能正常工作,給這個方法傳入4.015它將返回4.01而不是4.02,如我們在上面看到的   
  4.015*100=401.49999999999994   
  因此如果我們要做到精確的四捨五入,不能利用簡單型別做任何運算   
  java.text.DecimalFormat也不能解決這個問題:   

  System.out.println(new   java.text.DecimalFormat("0.00").format(4.025));   
  輸出是4.02   



  BigDecimal   
  在《Effective   Java》這本書中也提到這個原則,float和double只能用來做科學計算或者是工程計算,在商業計算中我們要用java.math.BigDecimal。BigDecimal一共有4個夠造方法,我們不關心用BigInteger來夠造的那兩個,那麼還有兩個,它們是:   
  BigDecimal(double   val)     
                      Translates   a   double   into   a   BigDecimal.     
  BigDecimal(String   val)     
                      Translates   the   String   repre   sentation   of   a   BigDecimal   into   a   BigDecimal.   

  上面的API簡要描述相當的明確,而且通常情況下,上面的那一個使用起來要方便一些。我們可能想都不想就用上了,會有什麼問題呢?等到出了問題的時候,才發現上面哪個夠造方法的詳細說明中有這麼一段:   
  Note:   the   results   of   this   constructor   can   be   somewhat   unpredictable.   One   might   assume   that   new   BigDecimal(.1)   is   exactly   equal   to   .1,   but   it   is   actually   equal   to   .1000000000000000055511151231257827021181583404541015625.   This   is   so   because   .1   cannot   be   represented   exactly   as   a   double   (or,   for   that   matter,   as   a   binary   fraction   of   any   finite   length).   Thus,   the   long   value   that   is   being   passed   in   to   the   constructor   is   not   exactly   equal   to   .1,   appearances   nonwithstanding.     
  The   (String)   constructor,   on   the   other   hand,   is   perfectly   predictable:   new   BigDecimal(".1")   is   exactly   equal   to   .1,   as   one   would   expect.   Therefore,   it   is   generally   recommended   that   the   (String)   constructor   be   used   in   preference   to   this   one.   

  原來我們如果需要精確計算,非要用String來夠造BigDecimal不可!在《Effective   Java》一書中的例子是用String來夠造BigDecimal的,但是書上卻沒有強調這一點,這也許是一個小小的失誤吧。   


  解決方案   
  現在我們已經可以解決這個問題了,原則是使用BigDecimal並且一定要用String來夠造。   
  但是想像一下吧,如果我們要做一個加法運算,需要先將兩個浮點數轉為String,然後夠造成BigDecimal,在其中一個上呼叫add方法,傳入另一個作為引數,然後把運算的結果(BigDecimal)再轉換為浮點數。你能夠忍受這麼煩瑣的過程嗎?下面我們提供一個工具類Arith來簡化操作。它提供以下靜態方法,包括加減乘除和四捨五入:   
  public   static   double   add(double   v1,double   v2)   
  public   static   double   sub(double   v1,double   v2)   
  public   static   double   mul(double   v1,double   v2)   
  public   static   double   div(double   v1,double   v2)   
  public   static   double   div(double   v1,double   v2,int   scale)   
  public   static   double   round(double   v,int   scale)   



  附錄   


  原始檔Arith.java:   

  import   java.math.BigDecimal;   
  /**   
    *   由於Java的簡單型別不能夠精確的對浮點數進行運算,這個工具類提供精   
    *   確的浮點數運算,包括加減乘除和四捨五入。   
    */   

  public   class   Arith{   

          //預設除法運算精度   
          private   static   final   int   DEF_DIV_SCALE   =   10;   


          //這個類不能例項化   
          private   Arith(){   
          }   


          /**   
            *   提供精確的加法運算。   
            *   @param   v1   被加數   
            *   @param   v2   加數   
            *   @return   兩個引數的和   
            */   

          public   static   double   add(double   v1,double   v2){   
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));   
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));   
                  return   b1.add(b2).doubleValue();   
          }   

          /**   
            *   提供精確的減法運算。   
            *   @param   v1   被減數   
            *   @param   v2   減數   
            *   @return   兩個引數的差   
            */   

          public   static   double   sub(double   v1,double   v2){   
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));   
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));   
                  return   b1.subtract(b2).doubleValue();   
          }     

          /**   
            *   提供精確的乘法運算。   
            *   @param   v1   被乘數   
            *   @param   v2   乘數   
            *   @return   兩個引數的積   
            */   

          public   static   double   mul(double   v1,double   v2){   
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));   
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));   
                  return   b1.multiply(b2).doubleValue();   
          }   



          /**   
            *   提供(相對)精確的除法運算,當發生除不盡的情況時,精確到   
            *   小數點以後10位,以後的數字四捨五入。   
            *   @param   v1   被除數   
            *   @param   v2   除數   
            *   @return   兩個引數的商   
            */   

          public   static   double   div(double   v1,double   v2){   
                  return   div(v1,v2,DEF_DIV_SCALE);   
          }   



          /**   
            *   提供(相對)精確的除法運算。當發生除不盡的情況時,由scale引數指   
            *   定精度,以後的數字四捨五入。   
            *   @param   v1   被除數   
            *   @param   v2   除數   
            *   @param   scale   表示表示需要精確到小數點以後幾位。   
            *   @return   兩個引數的商   
            */   

          public   static   double   div(double   v1,double   v2,int   scale){   
                  if(scale<0){   
                          throw   new   IllegalArgumentException(   
                                  "The   scale   must   be   a   positive   integer   or   zero");   
                  }   
                  BigDecimal   b1   =   new   BigDecimal(Double.toString(v1));   
                  BigDecimal   b2   =   new   BigDecimal(Double.toString(v2));   
                  return   b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();   
          }   



          /**   
            *   提供精確的小數位四捨五入處理。   
            *   @param   v   需要四捨五入的數字   
            *   @param   scale   小數點後保留幾位   
            *   @return   四捨五入後的結果   
            */   

          public   static   double   round(double   v,int   scale){   
                  if(scale<0){   
                          throw   new   IllegalArgumentException(   
                                  "The   scale   must   be   a   positive   integer   or   zero");   
                  }   
                  BigDecimal   b   =   new   BigDecimal(Double.toString(v));   
                  BigDecimal   one   =   new   BigDecimal("1");   
                  return   b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();   
          }   
  };  

相關推薦

java double相加精度誤差

問題的提出:     編譯執行下面這個程式會看到什麼?     public   class   Test{             public   static   void   main(String   args[]){                     Sys

Java浮點數float,bigdecimal和double精確計算的精度誤差問題

1.double小數轉bigdecimal後四捨五入計算有誤差 案例: double g= 12.35; BigDecimal bigG=new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); //期望

解決 java double型別相加精度問題

package com.hxyl.action; import java.text.DecimalFormat; public class Test { public static void main(String[] args) { // DecimalFor

javadouble精度去哪了[轉]

blank ble 四舍五入 img http 得到 .html 浮點 .com 我們知道,浮點數值采用二進制系統表示,二進制系統無法精確表示1/10,就好像十進制無法精確表示1/3 。 所以,我們采用四舍五入/保留小數位方式以獲得精簡數值。 那麽,問題來了

java,js中小數計算精度誤差問題

sys OS 時有 無限 小數 java sdn 符號位 ava   在碰見這個問題之前我完全沒有這個概念,碰見了特此記錄;   項目js裏面中用到了number*0.2(其中number是一個整數,我測試的時候是259),得到的結果卻是51.800000000000000

java double型別加減運算時出現的精度丟失問題

問題由來: 今天在寫一個業務引數的時候,需要幾個數一起算一下最後返回一個浮點數。 一開始我就直接用強制型別轉化之後開始加加減減,後來發現總是會在末尾多出一些莫名的小數,這很明顯就是精度丟失問題,但是因為是要返回固定的資料型別,不能用Format轉成Strin

Javadouble型別精度丟失問題

  首先,之所以double浮點數型別中會出現精度不準確這個問題,是因為浮點數在計算機的儲存方法導致的,要想具體瞭解的話,可以檢視《計算機組成原理》這本書,裡面有比較詳盡的解答。   接下來由一道題來引出接下來要講的這個問題:   對任意給定的一個doubl

java中解決double計算精度不準確問題

現象: 因需要對比投資成功前與投資成功後前後的剩餘金額的斷言,在斷言的時候發現針對double型別計算時會出現精度計算問題: 例如使用程式碼如下: 如上圖會因精度導致斷言失敗。 最後使用 DecimalFormat 類將數字進行格式化,程式碼如下: Decimal

double相加(減)結果會有些誤差

前提介紹       今天在除錯程式碼的時候發現了一個double型別資料相減的有趣問題,148163.1 - 82692.09大家猜猜結果等於多少,經過除錯最終為5471.010000000009。 是不是很奇怪,下面將說明這其中的奧妙! 原因說明      doub

java double型別-加減乘除高精度運算

double型別-加減乘除高精度運算 // 進行加法運算  private  double add(double v1,double v2){           BigDecimal b1 = new BigDecimal(Double.toString(v1));  

Java double型別加法精度問題

近日在工作中使用double型別相加,發現結果精度出現問題。百度驗證後得到答案,特此記錄。double型別資料相加的時候直接使用+號,得到的結果會出現精度誤差所以需要使用BigDecimal函式進行運算double v1 = 4.5; double v2 = 4.55; Bi

Java:利用BigDecimal類巧妙處理Double型別精度丟失

[toc] ## 本篇要點 - 簡單描述浮點數十進位制轉二進位制精度丟失的原因。 - 介紹幾種建立BigDecimal方式的區別。 - 整理了高精度計算的工具類。 - 學習了阿里巴巴Java開發手冊關於BigDecimal比較相等的規定。 ## 經典問題:浮點數精度丟失 精度丟失的問題是在其他計算機語

float,double精度丟失問題

idt multipl sof zh-cn hid lose 減法 add hide 問題提出:   12.0f-11.9f = 0.10000038,"減不盡"為什麽?   8888.88*100 = 888887.9999999999 ??? 來自MSDN的解釋:h

51nod 1873 初中的算術【Java BigDecimal/高精度小數】

mes quest mathjax html value tex con question i++ 1873 初中的算術 基準時間限制:1 秒 空間限制:131072 KB 分值: 10 難度:2級算法題 收藏 關註 Noder現

java double 轉換成 #.00 格式String 防止科學計數法

public static String double2String(Double d){ return d==null? "0.00" : String.format("%.2f", d); } 在報文前邊加8位長度 public String dea

ceil以及double精度問題

Codeforces Round #518 (Div. 2)  A CF一道水題,總過不去 後面看了一下資料發現是精度出問題了 1000000000000000000 1 1 1000000000000000000這裡發現 ceil(1000000000000000000*1.0+1)

解決java中丟失精度問題

解決java中丟失精度問題       在電商專案中遇到的問題,正常的float、double資料進行運算會出現精度丟失問題,在涉及貨幣計算時會出現嚴重後果。 程式碼: System.out.println(0.0

java浮點數精度損失原理和解決

我所在的公司近期要做一個打賞的功能,比如說發一張照片其他人可以對這張照片進行打賞,給些小錢。我的工作是負責給客戶端下發打賞訊息。工作完工之後客戶端同學說有個問題,我下發的打賞金額是string型別的,他們覺得double才對。於是我就去找老大問這個能不能改成double型別,老大說這個應該

float精度誤差

一、表示範圍 float型別資料佔4位元組記憶體(4B),共32位(32bit),在計算機中按IEEE格式儲存:1bit(符號位),8bit(指數位),23bit(尾數位)。 所以,float的指數範圍為-127~+128。其中負指數決定浮點數所能表達的絕對值最小的非零數;而正指數決定浮點數所能

JAVA——Double.valueOf 與 parseDouble 的區別

Double.valueOf 的返回值型別為Double。 Double.parseDouble的返回值型別為double。 但是由於後期JAVA的自動的封裝和自動的解裝,使得這兩個函式區分並不明顯。 package a; import java.util.HashSet; impo