1. 为什么new BigDecimal(0.1)是个坑?
我刚入行Java那会儿,在财务系统里用new BigDecimal(0.1)计算金额,结果对账时差了3分钱,被财务追着骂了半个月。后来才知道,这个看似简单的构造函数藏着大坑——它本质上是用不精确的double类型在构造对象。
1.1 二进制浮点数的本质缺陷
计算机用二进制表示浮点数时,就像用1/2 + 1/4 + 1/8...的组合来逼近0.1这个十进制数。无论加多少项,永远无法精确等于0.1:
java复制System.out.println(0.1 + 0.2); // 输出0.30000000000000004
IEEE 754标准中,double类型用64位存储(1位符号+11位指数+52位尾数),这种存储方式导致很多简单十进制数在二进制下是无限循环小数。当这些不精确的二进制数再转回十进制时,就会出现精度丢失。
关键结论:所有用浮点数构造的BigDecimal,在构造时就已经带上了精度误差
1.2 构造函数对比实测
用三种方式构造0.1的BigDecimal,看看实际存储值:
java复制System.out.println(new BigDecimal(0.1));
// 输出:0.1000000000000000055511151231257827021181583404541015625
System.out.println(new BigDecimal("0.1"));
// 输出:0.1
System.out.println(BigDecimal.valueOf(0.1));
// 输出:0.1
第一个构造函数的输出值末尾带着一长串数字,这就是二进制无法精确表示0.1的铁证。而字符串构造和valueOf()方法却能得到精确值。
2. 正确使用BigDecimal的姿势
2.1 字符串构造法(推荐)
java复制// ✅ 正确做法
BigDecimal exact = new BigDecimal("0.1");
// ❌ 错误做法
BigDecimal wrong = new BigDecimal(0.1);
字符串构造器会直接解析字符串的十进制表示,完全绕过二进制浮点数转
