posts - 40,  comments - 187,  trackbacks - 0

          說(shuō)在前面的話

                  先祝各位看官在虎年里虎虎生威,財(cái)源廣進(jìn),萬(wàn)事如意!


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


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

          /*
           * 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 {
              
          /**  
               * 對(duì)double數(shù)據(jù)進(jìn)行取精度.  
               * <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數(shù)據(jù).  
               * 
          @param
           scale  
               *            精度位數(shù)(保留的小數(shù)位數(shù)).  
               * 
          @param
           roundingMode  
               *            精度取值方式.  
               * 
          @return
           精度計(jì)算后的數(shù)據(jù).  
               
          */

              
          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 值提供了算術(shù)運(yùn)算。由于 BigDecimal 對(duì)象是不可變的,這些方法中的每一個(gè)都會(huì)產(chǎn)生新的 BigDecimal 對(duì)象。
               * 因?yàn)閯?chuàng)建對(duì)象的開(kāi)銷(xiāo), BigDecimal 不適合于大量的數(shù)學(xué)計(jì)算,但設(shè)計(jì)它的目的是用來(lái)精確地表示小數(shù)。 
          */

              
              
          /**
               * 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
               * 盡量采用財(cái)務(wù)常用的四舍六入五取偶 即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();
              }

              
              
          /**  
               * 測(cè)試用的main方法.  
               *   
               * 
          @param
           args  
               *            運(yùn)行參數(shù).  
               
          */

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

                  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的小數(shù),則第2位就+1

                  System.out.println("-- ROUND_UP -- 只要第2位后面存在大于0的小數(shù),則第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位后面的所有小數(shù)   

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

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

                  System.out.println("-- OUND_CEILING -- 如果數(shù)字>0 則和ROUND_UP作用一樣 如果數(shù)字<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   
                  
          //
          如果數(shù)字>0 則和ROUND_DOWN作用一樣   
                  
          //如果數(shù)字<0 則和ROUND_UP作用一樣   

                  System.out.println("-- ROUND_FLOOR -- 如果數(shù)字>0 則和ROUND_DOWN作用一樣 如果數(shù)字<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位數(shù)字>=5,則第2位數(shù)字+1   
                  
          //備注:只看第3位數(shù)字的值,不會(huì)考慮第3位之后的小數(shù)的   

                  System.out.println("-- ROUND_HALF_UP -- 如果第3位數(shù)字>=5,則第2位數(shù)字+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位數(shù)字>=5,則做ROUND_UP   
                  
          //如果第3位數(shù)字<5,則做ROUND_DOWN   

                  System.out.println("-- ROUND_HALF_DOWN -- 如果第3位數(shù)字>=5,則做ROUND_UP,如果第3位數(shù)字<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位是偶數(shù),則做ROUND_HALF_DOWN   
                  
          //如果第3位是奇數(shù),則做ROUND_HALF_UP   

                  System.out.println("-- ROUND_HALF_EVEN -- 如果第3位是偶數(shù),則做ROUND_HALF_DOWN,如果第3位是奇數(shù), 則做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   
              }

          }


          再說(shuō)說(shuō)BigDecimal

          用于較小數(shù)的 BigDecimal

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

          用于加、減、乘和除的方法給 BigDecimal 值提供了算術(shù)運(yùn)算。由于 BigDecimal 對(duì)象是不可變的,這些方法中的每一個(gè)都會(huì)產(chǎn)生新的 BigDecimal 對(duì)象。因此,因?yàn)閯?chuàng)建對(duì)象的開(kāi)銷(xiāo), BigDecimal 不適合于大量的數(shù)學(xué)計(jì)算,但設(shè)計(jì)它的目的是用來(lái)精確地表示小數(shù)。如果您正在尋找一種能精確表示如貨幣量這樣的數(shù)值,則 BigDecimal 可以很好地勝任該任務(wù)。

          所有的 equals 方法都不能真正測(cè)試相等

          如浮點(diǎn)類(lèi)型一樣, BigDecimal 也有一些令人奇怪的行為。尤其在使用 equals() 方法來(lái)檢測(cè)數(shù)值之間是否相等時(shí)要小心。 equals() 方法認(rèn)為,兩個(gè)表示同一個(gè)數(shù)但換算值不同(例如, 100.00100.000 )的 BigDecimal 值是不相等的。然而, compareTo() 方法會(huì)認(rèn)為這兩個(gè)數(shù)是相等的,所以在從數(shù)值上比較兩個(gè) BigDecimal 值時(shí),應(yīng)該使用 compareTo() 而不是 equals()

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

          使用 BigDecimal 作為互換類(lèi)型

          SQL-92 包括 DECIMAL 數(shù)據(jù)類(lèi)型,它是用于表示定點(diǎn)小數(shù)的精確數(shù)字類(lèi)型,它可以對(duì)小數(shù)進(jìn)行基本的算術(shù)運(yùn)算。一些 SQL 語(yǔ)言喜歡稱此類(lèi)型為 NUMERIC 類(lèi)型,其它一些 SQL 語(yǔ)言則引入了 MONEY 數(shù)據(jù)類(lèi)型,MONEY 數(shù)據(jù)類(lèi)型被定義為小數(shù)點(diǎn)右側(cè)帶有兩位的小數(shù)。

          如果希望將數(shù)字存儲(chǔ)到數(shù)據(jù)庫(kù)中的 DECIMAL 字段,或從 DECIMAL 字段檢索值,則如何確保精確地轉(zhuǎn)換該數(shù)字?您可能不希望使用由 JDBC PreparedStatementResultSet 類(lèi)所提供的 setFloat()getFloat() 方法,因?yàn)楦↑c(diǎn)數(shù)與小數(shù)之間的轉(zhuǎn)換可能會(huì)喪失精確性。相反,請(qǐng)使用 PreparedStatementResultSetsetBigDecimal()getBigDecimal() 方法。

          對(duì)于 BigDecimal ,有幾個(gè)可用的構(gòu)造函數(shù)。其中一個(gè)構(gòu)造函數(shù)以雙精度浮點(diǎn)數(shù)作為輸入,另一個(gè)以整數(shù)和換算因子作為輸入,還有一個(gè)以小數(shù)的 String 表示作為輸入。要小心使用 BigDecimal(double) 構(gòu)造函數(shù),因?yàn)槿绻涣私馑瑫?huì)在計(jì)算過(guò)程中產(chǎn)生舍入誤差。請(qǐng)使用基于整數(shù)或 String 的構(gòu)造函數(shù)。

          構(gòu)造 BigDecimal 數(shù)

          對(duì)于 BigDecimal ,有幾個(gè)可用的構(gòu)造函數(shù)。其中一個(gè)構(gòu)造函數(shù)以雙精度浮點(diǎn)數(shù)作為輸入,另一個(gè)以整數(shù)和換算因子作為輸入,還有一個(gè)以小數(shù)的 String 表示作為輸入。要小心使用 BigDecimal(double) 構(gòu)造函數(shù),因?yàn)槿绻涣私馑瑫?huì)在計(jì)算過(guò)程中產(chǎn)生舍入誤差。請(qǐng)使用基于整數(shù)或 String 的構(gòu)造函數(shù)。

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

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

          在執(zhí)行這段似乎無(wú)害的代碼時(shí)會(huì)拋出一些令人迷惑不解的異常(這取決于具體的 JDBC 驅(qū)動(dòng)程序),因?yàn)?0.01 的雙精度近似值會(huì)導(dǎo)致大的換算值,這可能會(huì)使 JDBC 驅(qū)動(dòng)程序或數(shù)據(jù)庫(kù)感到迷惑。JDBC 驅(qū)動(dòng)程序會(huì)產(chǎn)生異常,但可能不會(huì)說(shuō)明代碼實(shí)際上錯(cuò)在哪里,除非意識(shí)到二進(jìn)制浮點(diǎn)數(shù)的局限性。相反,使用 BigDecimal("0.01")BigDecimal(1, 2) 構(gòu)造 BigDecimal 來(lái)避免這類(lèi)問(wèn)題,因?yàn)檫@兩種方法都可以精確地表示小數(shù)。


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

          Java 理論與實(shí)踐: 您的小數(shù)點(diǎn)到哪里去了?

          Java Double 精度問(wèn)題總結(jié)

          JAVA對(duì)double或者float的浮點(diǎn)數(shù)精度計(jì)算控制方法

           

                                                                           THE END

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

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

          常用鏈接

          留言簿(12)

          隨筆分類(lèi)(43)

          相冊(cè)

          收藏夾(7)

          朋友的博客

          電子資料

          搜索

          •  

          積分與排名

          • 積分 - 303182
          • 排名 - 193

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 克拉玛依市| 武鸣县| 达孜县| 临朐县| 辽宁省| 商都县| 蓝山县| 桐城市| 新兴县| 淮北市| 鹿邑县| 乌鲁木齐县| 七台河市| 尉氏县| 沐川县| 临夏市| 南通市| 肇庆市| 漯河市| 兴山县| 孟州市| 海盐县| 湖南省| 乡宁县| 襄垣县| 凉山| 长顺县| 西峡县| 安国市| 卓尼县| 枞阳县| 连山| 梁平县| 抚顺县| 自贡市| 中宁县| 上栗县| 金堂县| 汉中市| 蛟河市| 卢氏县|