Skip to content

P65 99%+ 开发者踩坑!BigDecimal 的 4 个致命陷阱(金融必看) ​

面试题:用 BigDecimal 做金额计算有哪些坑?

陷阱 1:用构造器 new BigDecimal(double) 精度丢失 ​

java
BigDecimal d = new BigDecimal(0.1);
// 结果:0.1000000000000000055511151231257827021181583404541015625

double 本身是二进制近似,直接 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 列"。

基于 VitePress 重建