P65 99%+ 开发者踩坑!BigDecimal 的 4 个致命陷阱(金融必看)
面试题:用 BigDecimal 做金额计算有哪些坑?
陷阱 1:用构造器 new BigDecimal(double) 精度丢失
java
BigDecimal d = new BigDecimal(0.1);
// 结果:0.1000000000000000055511151231257827021181583404541015625double 本身是二进制近似,直接 new 会保留误差。
✅ 正确:
java
BigDecimal d = new BigDecimal("0.1"); // 字符串构造,精确
BigDecimal d2 = BigDecimal.valueOf(0.1); // valueOf 内部用字符串陷阱 2:equals 与 compareTo 语义不同
java
new BigDecimal("1.0").equals(new BigDecimal("1.00")); // false!scale 不同
new BigDecimal("1.0").compareTo(new BigDecimal("1.00")); // 0,数值相等equals比较数值 + scale;- 业务比较金额相等要用
compareTo,用 equals 会误判。
陷阱 3:除法不指定精度会抛 ArithmeticException
java
new BigDecimal("1").divide(new BigDecimal("3"));
// ❌ ArithmeticException: Non-terminating decimal expansion✅ 必须指定精度和舍入方式:
java
new BigDecimal("1").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);陷阱 4:舍入模式选错导致金额错误
常见舍入模式:
HALF_UP:四舍五入(业务默认);HALF_DOWN:五舍六入;HALF_EVEN:银行家舍入(对半分时取偶数);DOWN:直接截断(向零);UP:远离零。
手续费、折扣计算要明确舍入模式,并统一规则,否则对账对不上。
附加坑(补充)
- 0 比较:
compareTo(BigDecimal.ZERO),不要用 equals; - 不要用 BigDecimal 做除法后直接存:先 round 再存储;
- 金额建议统一用**分(整数)**存储,展示层再转元;
setScale也要指定舍入模式,否则抛异常。
加分点
- 说出为什么 double 有误差:二进制无法精确表示 0.1;
- 提到金额规范:存储用分(long),计算用 BigDecimal,前端展示用字符串;
- 追问"数据库用什么类型":
DECIMAL(18,2)而不是 double/float。
一句话总结
BigDecimal 四大坑:double 构造丢精度、equals 不等于数值相等(用 compareTo)、除法必须指定精度和舍入模式、舍入模式选错算错钱;金额场景统一"字符串构造 + 分存储 + DECIMAL 列"。