为什么BigDecimal("20")与BigDecimal("2E+1")不相等?
Why aren't
2E+1 and 20 equal in BigDecimal? Great question! 很多人第一次接触BigDecimal的equals()方法时,都会对2E+1和20不相等这种情况感到困惑——毕竟从数学上看它们完全是同一个数。让我们从BigDecimal的设计逻辑入手,一步步解开这个谜团。
The Core Rule of BigDecimal's equals() Method
BigDecimal的equals()方法不是单纯比较数学数值是否相等,它要求两个对象必须同时满足两个条件才会返回true:
- 数值上完全等价;
- 标度(scale)完全相同。
这里的「标度(scale)」指的是小数点后的位数,它是BigDecimal用来记录数值表示细节的关键属性——而2E+1和20的核心差异,恰恰就在这个属性上。
Breaking Down 2E+1 vs 20
让我们分别看这两个BigDecimal实例的内部属性:
对于
new BigDecimal("20"):- 标度(scale):
0(表示这个数没有小数部分); - 精度(precision):
2(有2个有效数字:2和0); - 内部存储逻辑:
unscaledValue = 20,scale = 0,计算为20 × 10^0 = 20。
- 标度(scale):
对于
new BigDecimal("2E+1"):- 标度(scale):
-1(因为科学计数法的指数是+1,BigDecimal会将其转换为unscaledValue × 10^(-scale)的形式,这里就是2 × 10^1 = 20); - 精度(precision):
1(只有2是有效数字,末尾的0是指数带来的占位符); - 内部存储逻辑:
unscaledValue = 2,scale = -1,计算为2 × 10^(-(-1)) = 20。
- 标度(scale):
显然,这两个实例的标度完全不同,所以equals()会返回false——哪怕它们的数学数值一模一样。
Why This Design Makes Sense
BigDecimal的设计初衷是精确表示十进制数,同时保留数值的语义信息,而标度和精度就是承载这些语义的关键:
- 语义区分:比如
20可能表示精确到个位的测量结果(两个有效数字),而2E+1可能表示精确到十位的测量结果(只有一个有效数字)——在科学计算或统计场景中,这种精度差异是有实际意义的; - 格式化与输出:标度会影响数值的字符串表示,
20会输出为"20",而2E+1默认会输出为"2E+1",这对应了不同的输入/输出场景; - 运算行为:在除法、舍入等运算中,标度会直接影响结果的精度和舍入规则,保留标度信息能确保运算符合预期的业务逻辑(比如财务系统中精确到分的金额,标度必须是2)。
How to Compare Numerical Equality
正如你提到的,如果你只需要比较数学数值是否相等,可以用两种方法:
- 使用
compareTo()方法:它只关注数值大小,返回0就表示两个数在数学上相等,完全忽略标度和精度; - 结合
stripTrailingZeros()和equals():stripTrailingZeros()会去除末尾的无效零并调整标度,比如new BigDecimal("20").stripTrailingZeros()会变成标度为-1的实例,这时候和2E+1调用equals()就会返回true。
内容的提问来源于stack exchange,提问作者Jan Gassen
相关产品推荐
相关产品推荐

