项目场景:
最近,再做支付相关的东西,前端展示相关金额的商品,然后提交到后端。前端展示的商品价格只支持到角,所以只保留一位小数。前端用到的是controller+velocity模板,展示的金额,是在controller中用BigDecimal相关方法格式化的。例如
Double aa = 1.2;
BigDecimal bigDecimalOne = new BigDecimal(aa).setScale(1, BigDecimal.ROUND_DOWN);
执行结果输出为1.1;
用户在提交订单后,后台服务还要重新计算一下商品价格,例子如下
Double aa = 1.2;
BigDecimal bigDecimalTwo = new BigDecimal(aa + "").setScale(1, BigDecimal.ROUND_DOWN);
执行结果输出变为1.2;
前后两个结果不一致这是为什么呢
原因分析:
- 通过a + ‘’ 将 double 值隐式转换为字符串(如 “11.2”)BigDecimal 通过字符串构造时,会精确存储十进制值(11.2)
- 而通过double类型构造时,double 类型存储的 11.2 是二进制近似值,实际值为:11.199999999999999289457264239899814060211181640625,所以保留1位截断的时候为11.1
- 根本原因就是,二进制浮点数缺陷:double 无法精确表示十进制小数(如 11.2),实际存储的是近似值。
解决方案:
处理浮点数时,永远避免直接使用 new BigDecimal(double)。优先选择:BigDecimal.valueOf(double)或者
new BigDecimal(String)。以保证精确的十进制表示。