不能直接用 double 做金融计算,因其二进制浮点表示无法精确存储十进制小数(如 0.1),导致累加、比较出错;应使用 BigDecimal,且须用字符串或 valueOf 创建,四则运算调用对应方法,除法需指定精度与舍入模式,比较用 compareTo。

为什么不能直接用 double 做金融计算
因为 double 是 IEEE 754 浮点数,二进制无法精确表示很多十进制小数(比如 0.1),导致累加、比较时出现不可预测的误差。比如 0.1 + 0.2 == 0.3 在 double 中返回 false。金融、计费、会计等场景必须避免这种误差,BigDecimal 是唯一可靠选择。
创建 BigDecimal 的正确方式:别用 double 构造器
这是最常踩的坑:new BigDecimal(0.1) 看似简洁,实则引入了 double 的原始误差。0.1 在 double 中实际存储为近似值,再转成 BigDecimal 只是“精确地保存了错误”。
正确做法只有两种:
- 用字符串构造:
new BigDecimal("0.1") - 用整数+标度构造:
BigDecimal.valueOf(1, 1)(等价于new BigDecimal("0.1"))
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal sum = a.add(b); // 正确得到 "0.3"
// ❌ 不要写:new BigDecimal(0.1)四则运算必须用方法,不能用 + - * /
BigDecimal 是不可变对象,没有重载算术操作符。所有运算都通过方法调用完成,且返回新实例:
立即学习“Java免费学习笔记(深入)”;
- 加法:
a.add(b) - 减法:
a.subtract(b) - 乘法:
a.multiply(b) - 除法:
a.divide(b, scale, roundingMode)—— 必须显式指定精度和舍入模式
除法尤其危险:a.divide(b) 若结果无限不循环(如 1/3),会抛 ArithmeticException。必须提供 scale(小数位数)和 RoundingMode(如 RoundingMode.HALF_UP)。
BigDecimal x = new BigDecimal("1");
BigDecimal y = new BigDecimal("3");
// ✅ 正确:指定精度和舍入
BigDecimal result = x.divide(y, 2, RoundingMode.HALF_UP); // "0.33"
// ❌ 错误:不指定会抛 ArithmeticException
// x.divide(y);比较大小别用 == 或 equals(),要用 compareTo()
== 比较引用,equals() 会同时比较数值和标度(scale),导致 new BigDecimal("1.0").equals(new BigDecimal("1")) 返回 false —— 这在金额判断中极易出错。
统一用 compareTo():
-
a.compareTo(b) == 0→ 数值相等 -
a.compareTo(b) > 0→ a 大于 b a.compareTo(b) → a 小于 b
另外,compareTo() 对 null 敏感,调用前务必判空。
真正难的不是记住这些规则,而是把它们变成肌肉记忆——尤其在快速迭代时,一个 new BigDecimal(0.1) 或漏掉 divide 的 RoundingMode,就可能让账单多扣一分钱,而这个错误在线上可能潜伏几周才暴露。










