這兩天,不斷的有人問我java中數值計算的精度問題,以前在項目中碰到過,吃了不少苦頭,但一直也沒管它,現在先小結一下,以后再慢慢補充吧,否則,過段時間又忘了。
java中的基本類型float有著很嚴重的精度缺失問題,這個我主要是通過java.math.BigDecimal來彌補,但BigDecimal畢竟是一個類,有著對象的創建銷毀等繁瑣的事情,況且java中類本身沒有destroy()方法,這就把一切對象的徹底銷毀后內存的回收,變成了一個不可測的變數,縱使你調用了system.gc(),但此方法的執行時機卻又是未知的;所以這就要求程序員要盡可能少的創建對象(當然這還與java本身的確非常消耗內存有關),當對象一旦不用,盡可能的置為null,否則當程序運行幾次后就會發現內存占有率高居不下,甚至有導致死機可能。BigDecimal還有一點要引起注意的是,兩個對象即使值確實相等,但它們相比較時也可能會引起不等的結果,舉個例子如:
BigDecimal testA = new BigDecimal(200)和BigDecimal testB = new BigDecimal(200.00),也許你認為testA.equals(testB)==true;但結果是false。然而testA.toString().equals(testB.toString())==true,這是因為testB.toString()的值為200,在進行類型轉換時,它已把小數點后的兩個零去掉了。為了解決這個問題,我通常的做法是將它們的小數點位數補起后再進行比較,這樣是肯定不會出錯了,如testA = testA.setScale(2,5);testB = testB.setScale(2,5);當然了你可以根據需要將小數點位數設多或設置少一點,setScale(int,int)中第一位數為設置的小數點位數,第二位是四舍五入的界值,你可以隨意修改,如6,就是大于等于6是就進位。還有testA = testA.setScale(2,5)這種寫法在用BigDecimal時將會一直出現,因為它必須要通過給自身返回值來替代已經存在的值,如果你這樣寫testA.add(new BigDecimal(0)),那你會發現你的結果并不是想象中的200,而是0。當然加減乘除都是一樣的。至于剛才的比較為何不用testA.floatValue(),當然還是因為會造成精度丟失的緣故了,當然如果你的小數位數不超過5位的話,也是可以的,如果是超過5位,那就和第五位的值有關了(因為它僅保留五位小數),顯示如果第五位大于等于五,即使第六位為零,也會在第五位上加一,否則后面的只會被截掉!
說到這里,忽然想起javascript中的尾數和精度問題,javascript中有一個方法“toFixed()”,這個方法就是用來截取小數點后尾數的長度的。例:var a = 343.12345465;var b = b.toFixed(4);這樣b小數點后的尾數就是4位了,注意,它是按照四舍五入進行截取的。