Java Float類型 減法運算時精度丟失問題
package test1; public class Test2 { /** } 果然輸出結果是: tttttt-----0.20000005 再測試了幾個float類型的減法,除了*.0這樣的相減沒有異議之外,都存在這個問題,就是說float在相減的時候精度丟失了。后來在網上找到一段解決這個問題的辦法,記在這里: package test1; import java.math.BigDecimal; public class Test2 { /** ssss----0.2 這樣一對比,差異就很明顯了。 解決了問題,再找了一下為什么會產生這種差異: 網上有篇文章寫得很詳細,標題為《剖析float型的內存存儲和精度丟失問題》,全文內容如下: 問題提出:12.0f-11.9f=0.10000038,"減不盡"為什么? 現在我們就詳細剖析一下浮點型運算為什么會造成精度丟失? 1、小數的二進制表示問題 首先我們要搞清楚下面兩個問題: (1) 十進制整數如何轉化為二進制數 算法很簡單。舉個例子,11表示成二進制數: 11/2=5 余 1 5/2=2 余 1 2/2=1 余 0 1/2=0 余 1 0結束 11二進制表示為(從下往上):1011 這里提一點:只要遇到除以后的結果為0了就結束了,大家想一想,所有的整數除以2是不是一定能夠最終得到0。換句話說,所有的整數轉變為二進制數的算法會不會無限循環下去呢?絕對不會,整數永遠可以用二進制精確表示 ,但小數就不一定了。 (2) 十進制小數如何轉化為二進制數 算法是乘以2直到沒有了小數為止。舉個例子,0.9表示成二進制數 0.9*2=1.8 取整數部分 1 0.8(1.8的小數部分)*2=1.6 取整數部分 1 0.6*2=1.2 取整數部分 1 0.2*2=0.4 取整數部分 0 0.4*2=0.8 取整數部分 0 0.8*2=1.6 取整數部分 1 0.6*2=1.2 取整數部分 0 ......... 0.9二進制表示為(從上往下): 1100100100100...... 注意:上面的計算過程循環了,也就是說*2永遠不可能消滅小數部分,這樣算法將無限下去。很顯然,小數的二進制表示有時是不可能精確的 。其實道理很簡單,十進制系統中能不能準確表示出1/3呢?同樣二進制系統也無法準確表示1/10。這也就解釋了為什么浮點型減法出現了"減不盡"的精度丟失問題。 2、 float型在內存中的存儲 眾所周知、 Java 的float型在內存中占4個字節。float的32個二進制位結構如下
float內存存儲結構 4bytes 31 30 29----23 22----0 表示 實數符號位 指數符號位 指數位 有效數位 其中符號位1表示正,0表示負。有效位數位24位,其中一位是實數符號位。 將一個float型轉化為內存存儲格式的步驟為: (1)先將這個實數的絕對值化為二進制格式,注意實數的整數部分和小數部分的二進制方法在上面已經探討過了。 舉例說明: 11.9的內存存儲格式 (1) 將11.9化為二進制后大約是" 1011. 1110011001100110011001100..."。 (2) 將小數點左移三位到第一個有效位右側: "1. 011 11100110011001100110 "。 保證有效位數24位,右側多余的截取(誤差在這里產生了 )。 (3) 這已經有了二十四位有效數字,將最左邊一位“1”去掉,得到“ 011 11100110011001100110 ”共23bit。將它放入float存儲結構的第22到第0位。 (4) 因為11.9是正數,因此在第31位實數符號位放入“0”。 (5) 由于我們把小數點左移,因此在第30位指數符號位放入“1”。 (6) 因為我們是把小數點左移3位,因此將3減去1得2,化為二進制,并補足7位得到0000010,放入第29到第23位。 最后表示11.9為: 0 1 0000010 011 11100110011001100110 再舉一個例子:0.2356的內存存儲格式 最后表示0.2356為:0 0 1111100 11100010100000100100000 將一個內存存儲的float二進制格式轉化為十進制的步驟: 3、浮點型的減法運算 浮點加減運算過程比定點運算過程復雜。完成浮點加減運算的操作過程大體分為四步: 如果判斷兩個需要加減的浮點數有一個為0,即可得知運算結果而沒有必要再進行有序的一些列操作。 (2) 比較階碼(指數位)大小并完成對階; 兩浮點數進行加減,首先要看兩數的 指數位 是否相同,即小數點位置是否對齊。若兩數 指數位 相同,表示小數點是對齊的,就可以進行尾數的加減運算。反之,若兩數階碼不同,表示小數點位置沒有對齊,此時必須使兩數的階碼相同,這個過程叫做對階 。 如何對 階(假設兩浮點數的指數位為 Ex 和 Ey ): 通過尾數的移位以改變 Ex 或 Ey ,使之相等。 由
于浮點表示的數多是規格化的,尾數左移會引起最高有位的丟失,造成很大誤差;而尾數右移雖引起最低有效位的丟失,但造成的誤差較小,因此,對階操作規定使
尾數右移,尾數右移后使階碼作相應增加,其數值保持不變。很顯然,一個增加后的階碼與另一個相等,所增加的階碼一定是小階。因此在對階時,總是使小階向大階看齊 ,即小階的尾數向右移位 ( 相當于小數點左移 ) ,每右移一位,其階碼加 1 ,直到兩數的階碼相等為止,右移的位數等于階差 △ E 。 對階完畢后就可 有效數位 求和。 不論是加法運算還是減法運算,都按加法進行操作,其方法與定點加減運算完全一樣。 略 4、 計算12.0f-11.9f 12.0f 的內存存儲格式為: 0 1 0000010 10000000000000000000000 11.9f 的內存存儲格式為: 0 1 0000010 011 11100110011001100110 可見兩數的指數位完全相同,只要對有效數位進行減法即可。 12.0f-11.9f 結果: 0 1 0000010 00000011001100110011010
將結果還原為十進制為: 0.000 11001100110011010= 0.10000038 |
posted on 2010-12-08 10:02 都市淘沙者 閱讀(13736) 評論(2) 編輯 收藏 所屬分類: Java Basic/Lucene/開源資料