不能直接用 double 构造 BigDecimal,因为 double 的二进制近似值(如 0.1 实为 0.10000000000000000555...)会被原样转为 BigDecimal,导致误差;应使用 String 构造器,注意清洗格式、避免空格逗号,并在 divide 时指定 scale 和 RoundingMode,比较时用 compareTo 而非 equals。

为什么不能直接用 double 构造 BigDecimal
因为 new BigDecimal(0.1) 会把已经失真的 double 值(如 0.10000000000000000555...)原样转成 BigDecimal,误差照单全收。这不是 BigDecimal 的问题,而是 double 在传入前就错了。你看到的 0.1 只是字面量表象,JVM 解析时已变成二进制近似值。
必须用 String 构造器的三个实操要点
-
new BigDecimal("0.1")是最安全的方式——字符串不经过二进制转换,直接按十进制解析,无损还原原始意图 - 从数据库或 JSON 读出的数字字段,如果类型是
double或float,得先转成字符串再构造:new BigDecimal(String.valueOf(dbValue)),不能跳步 - 注意字符串格式:不能含空格、逗号、货币符号;
"1,000.00"会抛NumberFormatException,得先清洗
divide 方法必须指定精度和舍入模式
BigDecimal.divide(BigDecimal) 在除不尽时会直接抛 ArithmeticException,不是返回近似值。这和加减乘不同,是设计使然。
- 必须显式调用带三个参数的重载:
dividend.divide(divisor, scale, RoundingMode.HALF_UP) -
scale要根据业务定:金融常用 2(分),科学计算可能要 6 或更多 -
RoundingMode.HALF_UP是四舍五入的常规选择,但注意它和数学上“四舍六入五成双”不同,HALF_EVEN才对应后者
等值比较别用 equals,改用 compareTo
new BigDecimal("1.0").equals(new BigDecimal("1")) 返回 false,因为它们的 scale 不同(1 vs 0),但数值相等。这是最容易被忽略的坑。
- 所有数值相等性判断必须用
compareTo:a.compareTo(b) == 0 -
compareTo比较的是实际数值大小,不受scale影响 - 如果你需要保留特定小数位再比较(比如只比到分),先用
setScale统一精度
BigDecimal 的精度保障不是自动生效的,它依赖你每一步都避开 double 的污染、显式控制除法行为、用对比较方式。少一个环节,前面的字符串构造就白做了。










