背景
我們在開發産品時, 會遇到金額的問題. 與普通的計算不同, 涉及到錢的問題不馬虎, 多一分少一分肯定是不行的.
資料庫: 存儲金額時, bigint, 以分為機關.
場景
在支付一個訂單時, 企業支付一部分, 個人支付一部分.
如果發生退單并且産生手續費的情況, 這個手續費是針對這個訂單的.
是以企業和個人都需要承擔一部分手續費, 然後再退款到企業和個人賬戶上.
而計算手續費的比例, 必然要用到除法運算.
比如: 先算企業手續費:
``
企業手續費 = 手續費 * (企業支付金額 / 訂單總金額);
comFee = fee * ( comPayAmount / payAmount).
Long = Long * (BigDecimal / BigDecimal)
comFee = fee * comPercent;
Long = Long * BigDecimal
``
個人手續費就是個減法.
而精度問題會出現在兩個地方:
- 計算百分比
- BigDecimal轉long
分析
- 使用float
這種方式, 數字稍微大一點, 就會有精度問題, 會出現多一分少一分的情況.float compPercent = (float) companyPayAmount / (float) payAmount; long companyFee = (long) ((float) penaltyAmount * (compPercent));
- 使用BigDecimal
使用這種方式, 有兩個問題:BigDecimal compPercent = BigDecimal.valueOf(companyPayAmount).divide(BigDecimal.valueOf(payAmount)); long companyFee = BigDecimal.valueOf(penaltyAmount).multiply(compPercent).longValue();
除不盡會報錯,divide()
會出現舍棄小數點後的資料,結果不準确.longValue()
解決
BigDecimal compPercent = BigDecimal.valueOf(companyPayAmount).divide(BigDecimal.valueOf(payAmount), 20, BigDecimal.ROUND_HALF_EVEN);
long companyFee = BigDecimal.valueOf(penaltyAmount).multiply(compPercent).round(MathContext.DECIMAL64).longValue();
-
可以指定: 保留多少位小數, 我這裡取20位; 舍入的方式, 我這裡取4舍5入.divide
- 轉化為long類型時, 需要先做一次舍入, 再轉成long型.
round(MathContext.DECIMAL64).longValue();