Dict.CN 在線詞典, 英語學習, 在線翻譯

          都市淘沙者

          荔枝FM Everyone can be host

          統(tǒng)計

          留言簿(23)

          積分與排名

          優(yōu)秀學習網(wǎng)站

          友情連接

          閱讀排行榜

          評論排行榜

          Java Float類型 減法運算時精度丟失問題

          package test1;

          public class Test2 {

          /**
          * @param args
          */
          public static void main(String[] args) {
             Float xx = 2.0f;
             Float yy = 1.8f;
             Float tt = xx - yy;
             System.out.println("tttttt-----" + tt);

          }

          }

          果然輸出結(jié)果是: tttttt-----0.20000005

          再測試了幾個float類型的減法,除了*.0這樣的相減沒有異議之外,都存在這個問題,就是說float在相減的時候精度丟失了。后來在網(wǎng)上找到一段解決這個問題的辦法,記在這里:

          package test1;

          import java.math.BigDecimal;

          public class Test2 {

          /**
          * @param args
          */
          public static void main(String[] args) {
             Float xx = 2.2f;
             Float yy = 2.0f;
             Float tt = xx - yy;
            
            
          BigDecimal b1 = new BigDecimal(Float.toString(xx));
             BigDecimal b2 = new BigDecimal(Float.toString(yy));
             float ss = b1.subtract(b2).floatValue();
             System.out.println("ssss----" + ss);
             System.out.println("tttttt-----" + tt);
          }
          }
          輸出為:

          ssss----0.2
          tttttt-----0.20000005

          這樣一對比,差異就很明顯了。

          解決了問題,再找了一下為什么會產(chǎn)生這種差異:

          網(wǎng)上有篇文章寫得很詳細,標題為《剖析float型的內(nèi)存存儲和精度丟失問題》,全文內(nèi)容如下:

          問題提出:12.0f-11.9f=0.10000038,"減不盡"為什么?

          現(xiàn)在我們就詳細剖析一下浮點型運算為什么會造成精度丟失?

          1、小數(shù)的二進制表示問題

                 首先我們要搞清楚下面兩個問題:

               (1) 十進制整數(shù)如何轉(zhuǎn)化為二進制數(shù)

                     算法很簡單。舉個例子,11表示成二進制數(shù):

                               11/2=5 余   1

                                 5/2=2   余   1

                                 2/2=1   余   0

                                 1/2=0   余   1

                                    0結(jié)束         11二進制表示為(從下往上):1011

                    這里提一點:只要遇到除以后的結(jié)果為0了就結(jié)束了,大家想一想,所有的整數(shù)除以2是不是一定能夠最終得到0。換句話說,所有的整數(shù)轉(zhuǎn)變?yōu)槎M制數(shù)的算法會不會無限循環(huán)下去呢?絕對不會,整數(shù)永遠可以用二進制精確表示 ,但小數(shù)就不一定了。

                (2) 十進制小數(shù)如何轉(zhuǎn)化為二進制數(shù)

                     算法是乘以2直到?jīng)]有了小數(shù)為止。舉個例子,0.9表示成二進制數(shù)

                               0.9*2=1.8   取整數(shù)部分 1

                               0.8(1.8的小數(shù)部分)*2=1.6    取整數(shù)部分 1

                               0.6*2=1.2   取整數(shù)部分 1

                               0.2*2=0.4   取整數(shù)部分 0

                               0.4*2=0.8   取整數(shù)部分 0

                               0.8*2=1.6 取整數(shù)部分 1

                               0.6*2=1.2   取整數(shù)部分 0

                                        .........      0.9二進制表示為(從上往下): 1100100100100......

                     注意:上面的計算過程循環(huán)了,也就是說*2永遠不可能消滅小數(shù)部分,這樣算法將無限下去。很顯然,小數(shù)的二進制表示有時是不可能精確的 。其實道理很簡單,十進制系統(tǒng)中能不能準確表示出1/3呢?同樣二進制系統(tǒng)也無法準確表示1/10。這也就解釋了為什么浮點型減法出現(xiàn)了"減不盡"的精度丟失問題。

          2、 float型在內(nèi)存中的存儲

               眾所周知、 Java 的float型在內(nèi)存中占4個字節(jié)。float的32個二進制位結(jié)構(gòu)如下

                    

          float內(nèi)存存儲結(jié)構(gòu)

                       4bytes      31    30    29----23    22----0         

                                  表示       實數(shù)符號位    指數(shù)符號位        指數(shù)位          有效數(shù)位

                  其中符號位1表示正,0表示負。有效位數(shù)位24位,其中一位是實數(shù)符號位。

                   將一個float型轉(zhuǎn)化為內(nèi)存存儲格式的步驟為:

                  (1)先將這個實數(shù)的絕對值化為二進制格式,注意實數(shù)的整數(shù)部分和小數(shù)部分的二進制方法在上面已經(jīng)探討過了。
               (2)將這個二進制格式實數(shù)的小數(shù)點左移或右移n位,直到小數(shù)點移動到第一個有效數(shù)字的右邊。
               (3)從小數(shù)點右邊第一位開始數(shù)出二十三位數(shù)字放入第22到第0位。
               (4)如果實數(shù)是正的,則在第31位放入“0”,否則放入“1”。
               (5)如果n 是左移得到的,說明指數(shù)是正的,第30位放入“1”。如果n是右移得到的或n=0,則第30位放入“0”。
               (6)如果n是左移得到的,則將n減去1后化為二進制,并在左邊加“0”補足七位,放入第29到第23位。如果n是右移得到的或n=0,則將n化為二進制后在左邊加“0”補足七位,再各位求反,再放入第29到第23位。

                    舉例說明: 11.9的內(nèi)存存儲格式

                 (1) 將11.9化為二進制后大約是" 1011. 1110011001100110011001100..."。

                 (2) 將小數(shù)點左移三位到第一個有效位右側(cè): "1. 011 11100110011001100110 "。 保證有效位數(shù)24位,右側(cè)多余的截?。?span style="color: #ff0000;">誤差在這里產(chǎn)生了 )。

                 (3) 這已經(jīng)有了二十四位有效數(shù)字,將最左邊一位“1”去掉,得到“ 011 11100110011001100110 ”共23bit。將它放入float存儲結(jié)構(gòu)的第22到第0位。

                 (4) 因為11.9是正數(shù),因此在第31位實數(shù)符號位放入“0”。

                 (5) 由于我們把小數(shù)點左移,因此在第30位指數(shù)符號位放入“1”。

                 (6) 因為我們是把小數(shù)點左移3位,因此將3減去1得2,化為二進制,并補足7位得到0000010,放入第29到第23位。

                     最后表示11.9為: 0 1 0000010 011 11100110011001100110

                     再舉一個例子:0.2356的內(nèi)存存儲格式
                (1)將0.2356化為二進制后大約是0.00111100010100000100100000。
                (2)將小數(shù)點右移三位得到1.11100010100000100100000。
                (3)從小數(shù)點右邊數(shù)出二十三位有效數(shù)字,即11100010100000100100000放
          入第22到第0位。
                (4)由于0.2356是正的,所以在第31位放入“0”。
                (5)由于我們把小數(shù)點右移了,所以在第30位放入“0”。
                (6)因為小數(shù)點被右移了3位,所以將3化為二進制,在左邊補“0”補足七
          位,得到0000011,各位取反,得到1111100,放入第29到第23位。
                 

                     最后表示0.2356為:0 0 1111100 11100010100000100100000

                    將一個內(nèi)存存儲的float二進制格式轉(zhuǎn)化為十進制的步驟:
               (1)將第22位到第0位的二進制數(shù)寫出來,在最左邊補一位“1”,得到二十四位有效數(shù)字。將小數(shù)點點在最左邊那個“1”的右邊。
               (2)取出第29到第23位所表示的值n。當30位是“0”時將n各位求反。當30位是“1”時將n增1。
               (3)將小數(shù)點左移n位(當30位是“0”時)或右移n位(當30位是“1”時),得到一個二進制表示的實數(shù)。
               (4)將這個二進制實數(shù)化為十進制,并根據(jù)第31位是“0”還是“1”加上正號或負號即可。

          3、浮點型的減法運算

                   浮點加減運算過程比定點運算過程復雜。完成浮點加減運算的操作過程大體分為四步:
          (1) 0操作數(shù)的檢查;

                         如果判斷兩個需要加減的浮點數(shù)有一個為0,即可得知運算結(jié)果而沒有必要再進行有序的一些列操作。

          (2) 比較階碼(指數(shù)位)大小并完成對階;

                          兩浮點數(shù)進行加減,首先要看兩數(shù)的 指數(shù)位 是否相同,即小數(shù)點位置是否對齊。若兩數(shù) 指數(shù)位 相同,表示小數(shù)點是對齊的,就可以進行尾數(shù)的加減運算。反之,若兩數(shù)階碼不同,表示小數(shù)點位置沒有對齊,此時必須使兩數(shù)的階碼相同,這個過程叫做對階 。

                         如何對 階(假設兩浮點數(shù)的指數(shù)位為 Ex 和 Ey ):

                  通過尾數(shù)的移位以改變 Ex 或 Ey ,使之相等。 由 于浮點表示的數(shù)多是規(guī)格化的,尾數(shù)左移會引起最高有位的丟失,造成很大誤差;而尾數(shù)右移雖引起最低有效位的丟失,但造成的誤差較小,因此,對階操作規(guī)定使 尾數(shù)右移,尾數(shù)右移后使階碼作相應增加,其數(shù)值保持不變。很顯然,一個增加后的階碼與另一個相等,所增加的階碼一定是小階。因此在對階時,總是使小階向大階看齊 ,即小階的尾數(shù)向右移位 ( 相當于小數(shù)點左移 ) ,每右移一位,其階碼加 1 ,直到兩數(shù)的階碼相等為止,右移的位數(shù)等于階差 △ E
          (3) 尾數(shù)(有效數(shù)位)進行加或減運算;

                         對階完畢后就可 有效數(shù)位 求和。 不論是加法運算還是減法運算,都按加法進行操作,其方法與定點加減運算完全一樣。
          (4) 結(jié)果規(guī)格化并進行舍入處理。

                         

          4、 計算12.0f-11.9f

                 12.0f 的內(nèi)存存儲格式為: 0 1 0000010 10000000000000000000000    

               11.9f 的內(nèi)存存儲格式為:   0 1 0000010 011 11100110011001100110

               可見兩數(shù)的指數(shù)位完全相同,只要對有效數(shù)位進行減法即可。

               12.0f-11.9f   結(jié)果:         0 1 0000010 00000011001100110011010

                

               將結(jié)果還原為十進制為: 0.000 11001100110011010= 0.10000038

          posted on 2010-12-08 10:02 都市淘沙者 閱讀(13736) 評論(2)  編輯  收藏 所屬分類: Java Basic/Lucene/開源資料

          評論

          # re: Java Float類型 減法運算時精度丟失問題 2015-07-10 19:19 xu

          感謝,網(wǎng)上不少資料直接往里放double,導致減法運算依然丟失。此樓正解。  回復  更多評論   

          # re: Java Float類型 減法運算時精度丟失問題 2016-08-01 19:33 飯飯

          3000000.0f-85.1f
          試試
          就不正確了  回復  更多評論   

          主站蜘蛛池模板: 福海县| 蓝田县| 宜兰县| 东兰县| 中山市| 巫溪县| 泰兴市| 景宁| 海丰县| 达日县| 西丰县| 铅山县| 炎陵县| 仪陇县| 会宁县| 武安市| 根河市| 勃利县| 财经| 三江| 镇坪县| 沙坪坝区| 苍梧县| 资溪县| 淮安市| 页游| 隆安县| 宁波市| 台中县| 长沙市| 和林格尔县| 昭平县| 大兴区| 漳平市| 察隅县| 山东| 呼伦贝尔市| 南乐县| 马山县| 六枝特区| 巴彦淖尔市|