背景,某个用户反馈线上后台提现金额到账少了一分钱,其输入的提现金额是19320.38(元),但实际银行到账金额1932037(分),也就是少了一分钱。然后立马翻看代码,以及运行日志。
伪代码如下:
less
@PostMapping("withDrawApply")
Result withdrawApply(@RequestBody Map<String,Object> param){
Object amount = params.get("amount");
toWithdrawApply(new BigDecimal(String.valueOf(amount));
}
Boolean toWithdrawApply(BigDecimal amount){
Map<String,Objct> param = new HashMap();
param.put("amount", amount.multiply(new BigDecimal(100)).intValue());
//TODO HTTP调用
//此处大意就是将提现参数提交给支付机构;其他参数省略
异步保存提现记录,此处提现记录中Record中的金额字段amount也取自:amount,保存的数据为19320.38
return flag;
}
通过日志排查,在提交给第三方支付机构的参数日志中,amount金额变成了1932079。 所以总结下过程就是:用户提交金额19320.80,然后double经过String.value,转BigDeciaml,最后乘以100,少了一分钱。
第一时间怀疑是精度缺失问题,springboot中param接收到的amount实际类型为double类型,而浮点数在二进制计算中并不能真实表示该确切的值,因此其底层可能是19320.7999999999....来表示
但是我后续经过进行模拟处理操作,进行同jdk版本,cpu架构环境的参数进行调用处理,一直不能重现。所以很好奇这到底是和什么原因有关?如果是精度缺失按道理每次都会重现吧? 如果是精度缺失问题,正常的处理方式应该是什么呢? 期待各位大佬答疑。