爭論不休的一個話題:金額到底是用Long還是BigDecimal?
在網(wǎng)上一直流傳著一個爭論不休的話題:金額到底是用Long還是用BigDecimal?這個話題一出在哪都會引起異常無比激烈的討論。。。。 比如說這個觀點(diǎn):算錢用BigDecimal是常識
圖片
圖片
有支持用Long的,將金額的單位設(shè)計(jì)為分,然后乘以100,使用Long進(jìn)行存儲以及計(jì)算,這樣不用擔(dān)心小數(shù)點(diǎn)問題。
圖片
并且一些銀行系統(tǒng)就會選擇用Long
圖片
還有,最最最牛逼的萬能大法:用String
圖片
成年人不做選擇題,Long跟BigDecimal都用。。。
圖片
還有一種就是封裝一個金額的基類,對金額進(jìn)行統(tǒng)一處理。
圖片
排除float和double
當(dāng)然,對于金額,首先我們要排除的就是float和double。它們不適合用于精確的金融計(jì)算,因?yàn)閒loat和double是基于IEEE 754標(biāo)準(zhǔn)的浮點(diǎn)數(shù)表示,它們無法精確地表示所有的十進(jìn)制小數(shù)。這會導(dǎo)致在進(jìn)行財(cái)務(wù)計(jì)算時出現(xiàn)舍入誤差,這些誤差可能會累積并導(dǎo)致不可預(yù)測的結(jié)果。
關(guān)于帶精度的計(jì)算,我們不推薦使用float以及double,推薦使用BigDecimal,具體原因請參考:聊一聊_BigDecimal_使用時的陷阱
選擇Long
Long類型在Java中用于存儲64位整數(shù)。它的主要優(yōu)點(diǎn)是速度快,因?yàn)檎麛?shù)運(yùn)算在CPU層面是非常高效的。另外,Long類型也占用較少的內(nèi)存,并且整數(shù)類型(BIGINT)在數(shù)據(jù)庫中占用較少的存儲空間。
但是Long類型在處理金額時有幾個明顯的缺點(diǎn):
1. 精度問題:Long只能存儲整數(shù),無法直接表示小數(shù)。使用Long來表示以分為單位的金額(例如,100表示1元),此時就會失去小數(shù)的精度。即使使用某種方式來表示小數(shù)(例如,乘以100或10000),也會遇到舍入誤差的問題。并且這種計(jì)算方式也會增加計(jì)算的復(fù)雜度。
2. 浮點(diǎn)數(shù)問題:雖然這不是直接使用Long的問題,但如果你嘗試將Long與浮點(diǎn)數(shù)(如double或float)進(jìn)行轉(zhuǎn)換以進(jìn)行計(jì)算(比如匯率計(jì)算等),還是會遇到浮點(diǎn)數(shù)精度問題,這可能導(dǎo)致在財(cái)務(wù)計(jì)算中出現(xiàn)不可接受的誤差。
在阿里巴巴的開發(fā)手冊中建議使用Long。
阿里巴巴開發(fā)手冊.png
但是在一些金融系統(tǒng)當(dāng)中,對小數(shù)位要求比較高的,比如精確到小數(shù)點(diǎn)后6位,那么我們使用Long進(jìn)行存儲,每次在計(jì)算時都要除以或者乘以1000000,那么計(jì)算的開銷就很大了。
并且,如果在需求確認(rèn)時,我們無法知道金額要求的小數(shù)位,那我們使用Long也是不行的,我們并不知道需要乘以或者除以多少個0。
選擇BigDecimal
BigDecimal是Java提供的一個類,用于任意精度的算術(shù)運(yùn)算。它的主要優(yōu)點(diǎn)是提供了高精度的計(jì)算,這對于金融和貨幣計(jì)算來說是非常重要的。BigDecimal可以表示任意大小的正數(shù)、負(fù)數(shù)或零,并可以精確控制舍入行為。并且在數(shù)據(jù)庫中存儲時也有對應(yīng)的類型進(jìn)行匹配,比如MySQL的DECIMAL類型提供了精確的數(shù)值存儲,可以匹配BigDecimal的精度。
但是BigDecimal也有一些缺點(diǎn):
- 1. 性能:與Long相比,BigDecimal的性能較差。因?yàn)樗倪\(yùn)算需要更多的內(nèi)存和CPU時間。
- 2. 復(fù)雜性:使用BigDecimal進(jìn)行運(yùn)算比使用Long或基本數(shù)據(jù)類型更復(fù)雜。你需要考慮舍入模式、精度等因素。
- 3. 在數(shù)據(jù)庫中需要更多的存儲空間來存儲小數(shù)部分。
而在Mysql的開發(fā)手冊中,建議金額需要進(jìn)行小數(shù)位計(jì)算時,存儲要使用Decimal,否則我們要將金額乘以對應(yīng)小數(shù)位的倍數(shù)變成BIGINT進(jìn)行存儲。
Mysql開發(fā)手冊.png
總結(jié)
基于上述對Long和BigDecimal的優(yōu)缺點(diǎn)分析,我們可以得出以下結(jié)論:
在金額計(jì)算層面,即代碼實(shí)現(xiàn)中,推薦使用BigDecimal進(jìn)行所有與金額相關(guān)的計(jì)算。BigDecimal提供了高精度的數(shù)值運(yùn)算,能夠確保金額計(jì)算的精確性,避免了因浮點(diǎn)數(shù)精度問題導(dǎo)致的財(cái)務(wù)誤差。使用BigDecimal可以簡化代碼邏輯,減少因處理精度問題而引入的復(fù)雜性。
而在數(shù)據(jù)庫存儲方面,我們需要根據(jù)具體需求進(jìn)行權(quán)衡。如果業(yè)務(wù)需求已經(jīng)明確金額只需精確到分(如某些國家/地區(qū)的貨幣最小單位為分),并且我們確信不會涉及到需要更高精度的小數(shù)計(jì)算,那么可以使用Long類型進(jìn)行存儲,將金額轉(zhuǎn)換為最小貨幣單位(如分)進(jìn)行存儲。這樣可以節(jié)省存儲空間并提高查詢性能。
但是如果業(yè)務(wù)需求中金額的小數(shù)位數(shù)不確定,或者可能涉及多位小數(shù)的計(jì)算(如國際貨幣交易等),那么最好使用DECIMAL或NUMERIC類型進(jìn)行存儲。這些類型提供了精確的數(shù)值存儲,可以確保數(shù)據(jù)庫中的數(shù)據(jù)與應(yīng)用程序中的BigDecimal對象保持一致,避免數(shù)據(jù)轉(zhuǎn)換過程中可能引入的精度損失。