??float 和double 主要用于科學計算和工程計算崩掘。它們執(zhí)行二進制浮點算法,該算法經(jīng)過精心設(shè)計少办,能夠在很大范圍內(nèi)快速提供精確的近似值苞慢。但是,它們不能提供準確的結(jié)果凡泣,也不應(yīng)該在需要精確結(jié)果的地方使用枉疼。float和double類型特別不適合進行貨幣計算因為作為float和double不可能代表0.1(或者任何其他10的負冪)皮假。
??例如,假設(shè)你口袋里有1.03美元骂维,你消費了42美分惹资。你還剩下多少錢?下面是一個簡單的程序片段,試圖回答這個問題:
System.out.println(1.03 - 0.42);
??不幸的是航闺,它輸出了0.6100000000000001褪测。這不是一個特例。假設(shè)你口袋里有一美元潦刃,你買了9臺洗衣機侮措,每臺10美分。你能得到多少零錢?
System.out.println(1.00 - 9 * 0.10);
??根據(jù)這個程序片段乖杠,可以得到0.0999999999999999998美元分扎。
??您可能認為,只需在打印之前將結(jié)果四舍五入就可以解決這個問題胧洒,但不幸的是畏吓,這種方法并不總是有效。例如卫漫,假設(shè)你口袋里有一美元菲饼,你看到一個架子上有一排好吃的糖果,它們的價格僅僅是10美分列赎,20美分,30美分饼煞,以此類推诗越,直到1美元。每買一顆糖掺喻,從10美分的那顆開始感耙,直到你買不起貨架上的下一顆糖持隧。你買了多少糖果,換了多少零錢?這里有一個簡單的程序設(shè)計來解決這個問題:
??如果你運行這個程序只酥,你會發(fā)現(xiàn)你可以買得起三塊糖褥实,你還有0.399999999999999999美元。這是錯誤的答案!解決這個問題的正確方法是使用BigDecimal裂允、int或long進行貨幣計算损离。
??這里是前一個程序的一個簡單轉(zhuǎn)換,使用BigDecimal類型代替double绝编。注意僻澎,使用BigDecimal的字符串構(gòu)造函數(shù)而不是它的double構(gòu)造函數(shù)。這是為了避免在計算中引入不準確的值[Bloch05, Puzzle 2]:
??如果你運行修改后的程序十饥,你會發(fā)現(xiàn)你可以用剩下的0美元買四顆糖窟勃。這是正確答案。
??然而逗堵,使用BigDecimal有兩個缺點:它比使用原始算術(shù)類型要不方便得多秉氧,而且速度要慢得多。如果你只解決一個簡單的問題蜒秤,后一種缺點是無關(guān)緊要的汁咏,但前者可能會讓你煩惱。
??使用BigDecimal的另一種方法是使用int或long垦藏,這取決于涉及的數(shù)量梆暖,并自己跟蹤小數(shù)點。在這個例子中掂骏,最明顯的方法是用美分而不是美元來計算轰驳。下面是一個采用這種方法的簡單轉(zhuǎn)換:
??總之,對于任何需要精確答案的計算弟灼,不要使用float或double级解。如果希望系統(tǒng)跟蹤小數(shù)點,并且不介意不使用基本類型帶來的不便和成本田绑,請使用BigDecimal。
??使用BigDecimal的另一個好處是芒划,它可以完全控制舍入民逼,當執(zhí)行需要舍入的操作時,可以從八種舍入模式中進行選擇疮鲫。如果您使用合法的舍入行為執(zhí)行業(yè)務(wù)計算俊犯,這將非常方便瘫析。如果性能是最重要的咸包,那么您不介意自己跟蹤小數(shù)點烂瘫,而且數(shù)量不是太大坟比,可以使用int或long。如果數(shù)量不超過9位小數(shù)籍琳,可以使用int;如果不超過18位趋急,可以使用long呜达。如果數(shù)量可能超過18位查近,則使用BigDecimal嗦嗡。
本文寫于2019.7.18,歷時1天