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