posts - 40,  comments - 187,  trackbacks - 0

          說在前面的話

                  先祝各位看官在虎年里虎虎生威,財源廣進,萬事如意!


                  Java 語言支持兩種基本的浮點類型: floatdouble ,以及與它們對應的包裝類 FloatDouble 。它們都依據 IEEE 754 標準,該標準為 32 位浮點和 64 位雙精度浮點二進制小數定義了二進制標準。
                  但是在一些項目中,一些非整數值(如幾元和幾分這樣的小數)需要很精確。所以,不要用浮點數表示精確值。浮點數不是精確值,所以使用它們會導致舍入誤差。因此,使用浮點數來試圖表示象貨幣量這樣的精確數量不是一個好的想法。使用浮點數來進行元和分計算會得到災難性的后果。浮點數最好用來表示象測量值這類數值,這類值從一開始就不怎么精確。
                  所以一般對double類型進行運算時,做好對結果進行處理,然后拿這個值去做其他事情。 下面我們就用代碼來說明一下如何對浮點數進行精度計算,以double為例。


          詳細代碼(注釋很詳細 不做解釋了)  可點擊這里下載代碼

          /*
           * Copyright reserved 2010 by AllensLab
           * @project AllensLab
           * @date Feb 22, 2010
           
          */

          package cn.allen.tools;

          import
           java.math.BigDecimal;

          /**
           * RoundTool
           * 
          @author allen
           * @time 10:05:40 AM Feb 22, 2010
           
          */

          public class RoundTools {
              
          /**  
               * 對double數據進行取精度.  
               * <p>  
               * For example: <br>  
               * double value = 100.345678; <br>  
               * double ret = round(value,4,BigDecimal.ROUND_HALF_UP); <br>  
               * ret為100.3457 <br>  
               *   
               * 
          @param
           value  
               *            double數據.  
               * 
          @param
           scale  
               *            精度位數(保留的小數位數).  
               * 
          @param
           roundingMode  
               *            精度取值方式.  
               * 
          @return
           精度計算后的數據.  
               
          */

              
          public static double round(double value, int scale, int roundingMode) {
                  BigDecimal bd 
          = new
           BigDecimal(value);
                  bd 
          =
           bd.setScale(scale, roundingMode);
                  
          double d =
           bd.doubleValue();
                  bd 
          = null
          ;
                  
          return
           d;
              }

              
              
          public static BigDecimal roundAgain(double value, int scale, int roundingMode) {
                  BigDecimal bd 
          = new
           BigDecimal(value);
                  bd 
          =
           bd.setScale(scale, roundingMode);
                  
          return
           bd;
              }

              
              
          /* 用于加、減、乘和除的方法給 BigDecimal 值提供了算術運算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產生新的 BigDecimal 對象。
               * 因為創建對象的開銷, BigDecimal 不適合于大量的數學計算,但設計它的目的是用來精確地表示小數。 
          */

              
              
          /**
               * Addition
               * 
          @param number1
               * 
          @param
           number2
               * 
          @return

               * 
          @author allen
               * @date 10:42:47 AM Feb 22, 2010
               
          */

              
          public static double add(double number1, double number2, int newScale, int roundingMode) {
                  
          return
           BigDecimal.valueOf(number1).add(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
              }

              
              
          public static double add(int newScale, int roundingMode, double number1, double numbers) {
                  BigDecimal bd 
          = new
           BigDecimal(number1);
                  
          for (double number : numbers) 
          {
                      bd 
          =
           bd.add(BigDecimal.valueOf(number).setScale(newScale, roundingMode));
                  }

                  
          return bd.doubleValue();
              }

              
              
          /**
               * Subtraction
               * 
          @param number1
               * 
          @param
           number2
               * 
          @return

               * 
          @author allen
               * @date 10:45:36 AM Feb 22, 2010
               
          */

              
          public static double subtract(double number1, double number2, int newScale, int roundingMode) {
                  
          return
           BigDecimal.valueOf(number1).subtract(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
              }

              
              
          /**
               * Multiplication 
               * 
          @param number1
               * 
          @param
           number2
               * 
          @return

               * 
          @author allen
               * @date 10:46:23 AM Feb 22, 2010
               
          */

              
          public static double multiply(double number1, double number2, int newScale, int roundingMode) {
                  
          return
           BigDecimal.valueOf(number1).multiply(BigDecimal.valueOf(number2)).setScale(newScale, roundingMode).doubleValue();
                  
              }

              
              
          /**
               * Division
               * 盡量采用財務常用的四舍六入五取偶 即ROUND_HALF_EVEN
               * 
          @param number1
               * 
          @param
           number2
               * 
          @return

               * 
          @author allen
               * @date 10:47:12 AM Feb 22, 2010
               
          */

              
          public static double divide(double number1, double number2, int scale, int roundingMode) {
                  
          return
           BigDecimal.valueOf(number1).divide(BigDecimal.valueOf(number2), scale, roundingMode).doubleValue();
              }

              
              
          /**  
               * 測試用的main方法.  
               *   
               * 
          @param
           args  
               *            運行參數.  
               
          */

              
          public static void main(String[] args) {
                  
          //下面都以保留2位小數為例   

                  System.out.println(add(12.34112.34492, BigDecimal.ROUND_HALF_EVEN));
                  System.out.println(add(
          2, BigDecimal.ROUND_HALF_UP, 12.34612.344912.340112.345
          ));
                  System.out.println(subtract(
          12.344912.3412
          , BigDecimal.ROUND_HALF_EVEN));
                  System.out.println(multiply(
          12.34490.012
          , BigDecimal.ROUND_HALF_UP));
                  System.out.println(divide(
          11.34112.3462
          , BigDecimal.ROUND_HALF_EVEN));
                  
          //
          ROUND_UP   
                  
          //只要第2位后面存在大于0的小數,則第2位就+1

                  System.out.println("-- ROUND_UP -- 只要第2位后面存在大于0的小數,則第2位就+1 --");
                  System.out.println(round(
          12.34012, BigDecimal.ROUND_UP));//12.35   

                  System.out.println(round(-12.34012, BigDecimal.ROUND_UP));//-12.35   
                  
          //
          ROUND_DOWN   
                  
          //
          與ROUND_UP相反   
                  
          //直接舍棄第2位后面的所有小數   

                  System.out.println("-- ROUND_DOWN -- 直接舍棄第2位后面的所有小數 --");
                  System.out.println(round(
          12.3492, BigDecimal.ROUND_DOWN));//12.34   

                  System.out.println(round(-12.3492, BigDecimal.ROUND_DOWN));//-12.34   
                  
          //
          ROUND_CEILING   
                  
          //
          如果數字>0 則和ROUND_UP作用一樣   
                  
          //如果數字<0 則和ROUND_DOWN作用一樣   

                  System.out.println("-- OUND_CEILING -- 如果數字>0 則和ROUND_UP作用一樣 如果數字<0 則和ROUND_DOWN作用一樣 --");
                  System.out.println(round(
          12.34012, BigDecimal.ROUND_CEILING));//12.35   

                  System.out.println(round(-12.3492, BigDecimal.ROUND_CEILING));//-12.34   
                  
          //
          ROUND_FLOOR   
                  
          //
          如果數字>0 則和ROUND_DOWN作用一樣   
                  
          //如果數字<0 則和ROUND_UP作用一樣   

                  System.out.println("-- ROUND_FLOOR -- 如果數字>0 則和ROUND_DOWN作用一樣 如果數字<0 則和ROUND_UP作用一樣 --");
                  System.out.println(round(
          12.3492, BigDecimal.ROUND_FLOOR));//12.34   

                  System.out.println(round(-12.34012, BigDecimal.ROUND_FLOOR));//-12.35   
                  
          //
          ROUND_HALF_UP [這種方法最常用]   
                  
          //
          如果第3位數字>=5,則第2位數字+1   
                  
          //備注:只看第3位數字的值,不會考慮第3位之后的小數的   

                  System.out.println("-- ROUND_HALF_UP -- 如果第3位數字>=5,則第2位數字+1 --");
                  System.out.println(round(
          12.3452, BigDecimal.ROUND_HALF_UP));//12.35   

                  System.out.println(round(12.34492, BigDecimal.ROUND_HALF_UP));//12.34   
                  System.out.println(round(-12.3452, BigDecimal.ROUND_HALF_UP));//-12.35   
                  System.out.println(round(-12.34492, BigDecimal.ROUND_HALF_UP));//-12.34   
                  
          //
          ROUND_HALF_DOWN   
                  
          //
          如果第3位數字>=5,則做ROUND_UP   
                  
          //如果第3位數字<5,則做ROUND_DOWN   

                  System.out.println("-- ROUND_HALF_DOWN -- 如果第3位數字>=5,則做ROUND_UP,如果第3位數字<5,則做ROUND_DOWN --");
                  System.out.println(round(
          12.3452, BigDecimal.ROUND_HALF_DOWN));//12.35   

                  System.out.println(round(12.34492, BigDecimal.ROUND_HALF_DOWN));//12.34   
                  System.out.println(round(-12.3452, BigDecimal.ROUND_HALF_DOWN));//-12.35   
                  System.out.println(round(-12.34492, BigDecimal.ROUND_HALF_DOWN));//-12.34   
                  
          //
          ROUND_HALF_EVEN   
                  
          //
          如果第3位是偶數,則做ROUND_HALF_DOWN   
                  
          //如果第3位是奇數,則做ROUND_HALF_UP   

                  System.out.println("-- ROUND_HALF_EVEN -- 如果第3位是偶數,則做ROUND_HALF_DOWN,如果第3位是奇數, 則做ROUND_HALF_UP --");
                  System.out.println(round(
          12.3462, BigDecimal.ROUND_HALF_EVEN));//12.35   

                  System.out.println(round(12.3452, BigDecimal.ROUND_HALF_EVEN));//12.35   
              }

          }


          再說說BigDecimal

          用于較小數的 BigDecimal

          從 JDK 1.3 起,Java 開發人員就有了另一種數值表示法來表示非整數: BigDecimalBigDecimal 是標準的類,在編譯器中不需要特殊支持,它可以表示任意精度的小數,并對它們進行計算。在內部,可以用任意精度任何范圍的值和一個換算因子來表示 BigDecimal ,換算因子表示左移小數點多少位,從而得到所期望范圍內的值。因此,用 BigDecimal 表示的數的形式為 unscaledValue*10 -scale

          用于加、減、乘和除的方法給 BigDecimal 值提供了算術運算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產生新的 BigDecimal 對象。因此,因為創建對象的開銷, BigDecimal 不適合于大量的數學計算,但設計它的目的是用來精確地表示小數。如果您正在尋找一種能精確表示如貨幣量這樣的數值,則 BigDecimal 可以很好地勝任該任務。

          所有的 equals 方法都不能真正測試相等

          如浮點類型一樣, BigDecimal 也有一些令人奇怪的行為。尤其在使用 equals() 方法來檢測數值之間是否相等時要小心。 equals() 方法認為,兩個表示同一個數但換算值不同(例如, 100.00100.000 )的 BigDecimal 值是不相等的。然而, compareTo() 方法會認為這兩個數是相等的,所以在從數值上比較兩個 BigDecimal 值時,應該使用 compareTo() 而不是 equals()

          另外還有一些情形,任意精度的小數運算仍不能表示精確結果。例如, 1 除以 9 會產生無限循環的小數 .111111... 。出于這個原因,在進行除法運算時, BigDecimal 可以讓您顯式地控制舍入。 movePointLeft() 方法支持 10 的冪次方的精確除法。

          使用 BigDecimal 作為互換類型

          SQL-92 包括 DECIMAL 數據類型,它是用于表示定點小數的精確數字類型,它可以對小數進行基本的算術運算。一些 SQL 語言喜歡稱此類型為 NUMERIC 類型,其它一些 SQL 語言則引入了 MONEY 數據類型,MONEY 數據類型被定義為小數點右側帶有兩位的小數。

          如果希望將數字存儲到數據庫中的 DECIMAL 字段,或從 DECIMAL 字段檢索值,則如何確保精確地轉換該數字?您可能不希望使用由 JDBC PreparedStatementResultSet 類所提供的 setFloat()getFloat() 方法,因為浮點數與小數之間的轉換可能會喪失精確性。相反,請使用 PreparedStatementResultSetsetBigDecimal()getBigDecimal() 方法。

          對于 BigDecimal ,有幾個可用的構造函數。其中一個構造函數以雙精度浮點數作為輸入,另一個以整數和換算因子作為輸入,還有一個以小數的 String 表示作為輸入。要小心使用 BigDecimal(double) 構造函數,因為如果不了解它,會在計算過程中產生舍入誤差。請使用基于整數或 String 的構造函數。

          構造 BigDecimal 數

          對于 BigDecimal ,有幾個可用的構造函數。其中一個構造函數以雙精度浮點數作為輸入,另一個以整數和換算因子作為輸入,還有一個以小數的 String 表示作為輸入。要小心使用 BigDecimal(double) 構造函數,因為如果不了解它,會在計算過程中產生舍入誤差。請使用基于整數或 String 的構造函數。

          如果使用 BigDecimal(double) 構造函數不恰當,在傳遞給 JDBC setBigDecimal() 方法時,會造成似乎很奇怪的 JDBC 驅動程序中的異常。例如,考慮以下 JDBC 代碼,該代碼希望將數字 0.01 存儲到小數字段:

           PreparedStatement ps =
                      connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
                      ps.setString(1, "penny");
                      ps.setBigDecimal(2, new BigDecimal(0.01));
                      ps.executeUpdate();
                      

          在執行這段似乎無害的代碼時會拋出一些令人迷惑不解的異常(這取決于具體的 JDBC 驅動程序),因為 0.01 的雙精度近似值會導致大的換算值,這可能會使 JDBC 驅動程序或數據庫感到迷惑。JDBC 驅動程序會產生異常,但可能不會說明代碼實際上錯在哪里,除非意識到二進制浮點數的局限性。相反,使用 BigDecimal("0.01")BigDecimal(1, 2) 構造 BigDecimal 來避免這類問題,因為這兩種方法都可以精確地表示小數。


          本文參考了一下文章,對這些作者表示感謝!

          Java 理論與實踐: 您的小數點到哪里去了?

          Java Double 精度問題總結

          JAVA對double或者float的浮點數精度計算控制方法

           

                                                                           THE END

          posted on 2010-02-22 13:59 小立飛刀 閱讀(5513) 評論(0)  編輯  收藏 所屬分類: Others
          <2010年2月>
          31123456
          78910111213
          14151617181920
          21222324252627
          28123456
          78910111213

          生存或毀滅,這是個必答之問題:是否應默默的忍受坎苛命運之無情打擊,還是應與深如大海之無涯苦難奮然為敵,并將其克服。此二抉擇,究竟是哪個較崇高?

          常用鏈接

          留言簿(12)

          隨筆分類(43)

          相冊

          收藏夾(7)

          朋友的博客

          電子資料

          搜索

          •  

          積分與排名

          • 積分 - 302797
          • 排名 - 192

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 顺平县| 太仓市| 娱乐| 庆安县| 屏山县| 淄博市| 全椒县| 江口县| 佛山市| 碌曲县| 筠连县| 汝城县| 深州市| 噶尔县| 苏尼特左旗| 三原县| 肥东县| 体育| 信宜市| 思茅市| 宿迁市| 新晃| 台前县| 毕节市| 泾源县| 吉林省| 紫阳县| 卢龙县| 开原市| 莱阳市| 阿坝县| 盐山县| 西盟| 手游| 贺兰县| 广汉市| 小金县| 广德县| 平陆县| 无极县| 开阳县|