BigDecimal的精度问题以及解决⽅案
bigdecimal除法保留小数背景
我们在开发产品时, 会遇到⾦额的问题. 与普通的计算不同, 涉及到钱的问题不马虎, 多⼀分少⼀分肯定是不⾏的.
数据库: 存储⾦额时, bigint, 以分为单位.
场景
在⽀付⼀个订单时, 企业⽀付⼀部分, 个⼈⽀付⼀部分.
如果发⽣退单并且产⽣⼿续费的情况, 这个⼿续费是针对这个订单的.
所以企业和个⼈都需要承担⼀部分⼿续费, 然后再退款到企业和个⼈账户上.
⽽计算⼿续费的⽐例, 必然要⽤到除法运算.
⽐如: 先算企业⼿续费:
``
企业⼿续费 = ⼿续费 * (企业⽀付⾦额 / 订单总⾦额);
comFee = fee * ( comPayAmount / payAmount).
Long = Long * (BigDecimal / BigDecimal)
comFee = fee * comPercent;
Long = Long * BigDecimal
``
个⼈⼿续费就是个减法.
⽽精度问题会出现在两个地⽅:
1. 计算百分⽐
2. BigDecimal转long
分析
1. 使⽤float
float compPercent = (float) companyPayAmount / (float) payAmount;
long companyFee = (long) ((float) penaltyAmount * (compPercent));
这种⽅式, 数字稍微⼤⼀点, 就会有精度问题, 会出现多⼀分少⼀分的情况.
2. 使⽤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();
1. divide可以指定: 保留多少位⼩数, 我这⾥取20位; 舍⼊的⽅式, 我这⾥取4舍5⼊.
2. 转化为long类型时, 需要先做⼀次舍⼊, 再转成long型. round(MathContext.DECIMAL64).longValue();

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。