用 double/float 算钱会出错,因其二进制无法精确表示 0.1 等十进制小数,导致 0.1+0.2=0.30000000000000004;金融场景必须用 bigdecimal,且须用字符串构造、显式调用 add/subtract/multiply/divide 方法,除法需指定 scale 和 roundingmode(推荐 half_even)。

为什么 double 和 float 算钱会出错
因为它们底层用二进制表示十进制小数,很多常见小数(比如 0.1、0.05)根本无法精确存储。比如 0.1 + 0.2 在 Java 里结果是 0.30000000000000004,不是 0.3——这不是 bug,是 IEEE 754 浮点标准的必然表现。
金融、计费、精度敏感场景必须避开 double/float,改用 BigDecimal。
- 别用
new BigDecimal(double)构造,比如new BigDecimal(0.1),它会把已经失真的double值直接转成BigDecimal,误差照搬 - 正确做法是用字符串构造:
new BigDecimal("0.1") - 所有参与运算的数都得是
BigDecimal,混用int或double会触发隐式转换,悄悄引入误差
BigDecimal 的加减乘除必须用方法,不能用操作符
Java 不支持重载操作符,所以 +、-、*、/ 对 BigDecimal 无效。写 a + b 实际是调用 toString() 拼接字符串,完全不是计算。
必须显式调用对应方法:
立即学习“Java免费学习笔记(深入)”;
- 加法:
a.add(b) - 减法:
a.subtract(b) - 乘法:
a.multiply(b) - 除法:
a.divide(b, scale, roundingMode)—— 注意:除法必须指定精度和舍入模式,否则遇到无限循环小数(如1/3)直接抛ArithmeticException
例如:new BigDecimal("1").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP) 得到 0.33。
除法时 scale 和 RoundingMode 怎么选
金融系统通常要求“四舍五入到分”,也就是保留两位小数,且按银行标准舍入(非简单四舍五入)。这时候 scale = 2 是硬性要求,RoundingMode 推荐用 RoundingMode.HALF_EVEN(银行家舍入),它比 HALF_UP 更公平,能减少统计偏差。
-
RoundingMode.HALF_UP:传统四舍五入,0.125 → 0.13 -
RoundingMode.HALF_EVEN:遇 5 时向偶数舍入,0.125 → 0.12,0.135 → 0.14 - 千万别用
RoundingMode.UP或RoundingMode.DOWN,它们偏向一方,长期累计会导致账目偏差 - 如果不确定该用哪个,先查业务规范——计费系统常明确要求
HALF_EVEN,前端展示可放宽为HALF_UP
性能开销和线程安全要注意什么
BigDecimal 是不可变对象,每次运算都生成新实例,内存和 CPU 开销比 double 高得多。高频交易或批量计算中,这点不能忽略。
- 避免在循环里反复创建
BigDecimal,比如把new BigDecimal("0.01")提前定义为static final常量 -
BigDecimal是线程安全的(不可变),但它的运算结果不是“原子”行为,比如balance = balance.add(amount)这种赋值不是原子操作,多线程更新同一变量仍需同步 - 如果只是做中间计算、最终结果不落地,又对精度没极端要求,可以考虑先用
long存“分”(如100表示 1 元),全程整数运算,最后再转BigDecimal格式化输出
真正难的不是写对语法,而是想清楚在哪一层做精度控制:输入校验、中间计算、还是最终展示?漏掉任意一环,前面全白忙。









