爭論不休的一個話題:金額到底是用Long還是BigDecimal?

码农Academy發表於2024-04-18

在網上一直流傳著一個爭論不休的話題:金額到底是用Long還是用BigDecimal?這個話題一出在哪都會引起異常無比激烈的討論。。。。
比如說這個觀點:算錢用BigDecimal是常識

image.png
image.png

有支援用Long的,將金額的單位設計為分,然後乘以100,使用Long進行儲存以及計算,這樣不用擔心小數點問題。
image.png
並且一些銀行系統就會選擇用Long
image.png
還有,最最最牛逼的萬能大法:用String

image.png
成年人不做選擇題,Long跟BigDecimal都用。。。
image.png
還有一種就是封裝一個金額的基類,對金額進行統一處理。
image.png

排除float和double

當然,對於金額,首先我們要排除的就是float和double。它們不適合用於精確的金融計算,因為floatdouble是基於IEEE 754標準的浮點數表示,它們無法精確地表示所有的十進位制小數。這會導致在進行財務計算時出現舍入誤差,這些誤差可能會累積並導致不可預測的結果。

關於帶精度的計算,我們不推薦使用float以及double,推薦使用BigDecimal,具體原因請參考:聊一聊_BigDecimal_使用時的陷阱

選擇Long

Long型別在Java中用於儲存64位整數。它的主要優點是速度快,因為整數運算在CPU層面是非常高效的。另外,Long型別也佔用較少的記憶體,並且整數型別(BIGINT)在資料庫中佔用較少的儲存空間。

但是Long型別在處理金額時有幾個明顯的缺點:

  1. 精度問題Long只能儲存整數,無法直接表示小數。使用Long來表示以分為單位的金額(例如,100表示1元),此時就會失去小數的精度。即使使用某種方式來表示小數(例如,乘以100或10000),也會遇到舍入誤差的問題。並且這種計算方式也會增加計算的複雜度。
  2. 浮點數問題:雖然這不是直接使用Long的問題,但如果你嘗試將Long與浮點數(如doublefloat)進行轉換以進行計算(比如匯率計算等),還是會遇到浮點數精度問題,這可能導致在財務計算中出現不可接受的誤差。

在阿里巴巴的開發手冊中建議使用Long。
image.png
但是在一些金融系統當中,對小數位要求比較高的,比如精確到小數點後6位,那麼我們使用Long進行儲存,每次在計算時都要除以或者乘以1000000,那麼計算的開銷就很大了。

並且,如果在需求確認時,我們無法知道金額要求的小數位,那我們使用Long也是不行的,我們並不知道需要乘以或者除以多少個0。

選擇BigDecimal

BigDecimal是Java提供的一個類,用於任意精度的算術運算。它的主要優點是提供了高精度的計算,這對於金融和貨幣計算來說是非常重要的。BigDecimal可以表示任意大小的正數、負數或零,並可以精確控制舍入行為。並且在資料庫中儲存時也有對應的型別進行匹配,比如MySQL的DECIMAL型別提供了精確的數值儲存,可以匹配BigDecimal的精度。

但是BigDecimal也有一些缺點:

  1. 效能:與Long相比,BigDecimal的效能較差。因為它的運算需要更多的記憶體和CPU時間。
  2. 複雜性:使用BigDecimal進行運算比使用Long或基本資料型別更復雜。你需要考慮舍入模式、精度等因素。
  3. 在資料庫中需要更多的儲存空間來儲存小數部分。

而在Mysql的開發手冊中,建議金額需要進行小數位計算時,儲存要使用Decimal,否則我們要將金額乘以對應小數位的倍數變成BIGINT進行儲存。
image.png

總結

基於上述對LongBigDecimal的優缺點分析,我們可以得出以下結論:

在金額計算層面,即程式碼實現中,推薦使用BigDecimal進行所有與金額相關的計算。BigDecimal提供了高精度的數值運算,能夠確保金額計算的精確性,避免了因浮點數精度問題導致的財務誤差。使用BigDecimal可以簡化程式碼邏輯,減少因處理精度問題而引入的複雜性。

而在資料庫儲存方面,我們需要根據具體需求進行權衡。如果業務需求已經明確金額只需精確到分(如某些國家/地區的貨幣最小單位為分),並且我們確信不會涉及到需要更高精度的小數計算,那麼可以使用Long型別進行儲存,將金額轉換為最小貨幣單位(如分)進行儲存。這樣可以節省儲存空間並提高查詢效能。

但是如果業務需求中金額的小數位數不確定,或者可能涉及多位小數的計算(如國際貨幣交易等),那麼最好使用DECIMALNUMERIC型別進行儲存。這些型別提供了精確的數值儲存,可以確保資料庫中的資料與應用程式中的BigDecimal物件保持一致,避免資料轉換過程中可能引入的精度損失。

本文已收錄於我的個人部落格:碼農Academy的部落格,專注分享Java技術乾貨,包括Java基礎、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中介軟體、架構設計、面試題、程式設計師攻略等

相關文章