BigDecimal构造器类型差异导致JUnit断言成功/失败的原因
为什么BigDecimal构造器参数类型会影响JUnit断言结果?
核心原因是double类型的二进制浮点精度缺陷,这是Java浮点类型的固有问题,和JUnit本身无关。
两个构造器的本质区别
new BigDecimal(String s):会精确解析字符串中的十进制数值,完全复刻你输入的小数,得到的BigDecimal是严格精确的。比如new BigDecimal("2.55")就是实打实的2.55。new BigDecimal(double d):会直接把double类型的二进制近似值转成BigDecimal。问题在于,很多十进制小数(比如2.55)无法用二进制浮点精确表示,存成double时已经是一个接近2.55但略有偏差的数值(实际是2.5500000000000000444089209850062616169452667236328125这类近似值),所以用这个构造器得到的BigDecimal根本不是你直观以为的2.55。
结合你的代码分析
你的计算逻辑里,除了spendingOne = new BigDecimal(0.25),其他都是用String构造的精确值。0.25之所以没问题,是因为它是2的负二次方,二进制可以精确表示,所以new BigDecimal(0.25)得到的是精确值。最终计算出来的finalBalance是精确的2.55。
当你用assertThat(finalBalance, is(new BigDecimal("2.55")))时,两边都是精确的2.55,自然断言通过。但用new BigDecimal(2.55)时,右边是那个带偏差的近似值,而BigDecimal的equals方法会同时比较数值和精度,两者不相等,断言就失败了。
解决方案
处理精确小数计算(比如货币、金额类场景)时,永远优先用String构造器创建BigDecimal。如果不得不从double转换,推荐用BigDecimal.valueOf(double)方法——它会先把double转成对应的十进制字符串再解析,得到的结果更符合直观预期。
比如把断言改成这样就能通过:
assertThat(finalBalance, is(BigDecimal.valueOf(2.55)));
附你提供的测试代码
断言通过的版本(String构造器)
@Test public void areCalculationsDoingOkWithBigDecimal(){ BigDecimal initialBalance = new BigDecimal(5); BigDecimal spendingOne = new BigDecimal(0.25); BigDecimal spendingTwo = new BigDecimal("0.47"); BigDecimal spendingThree = new BigDecimal ("1.73"); BigDecimal finalBalance = initialBalance.subtract(spendingOne).subtract(spendingTwo).subtract(spendingThree); System.out.println("Final balance after all the spendings: " + finalBalance); assertThat(finalBalance, is(new BigDecimal("2.55"))); }
断言失败的版本(double构造器)
@Test public void areCalculationsDoingOkWithBigDecimal(){ BigDecimal initialBalance = new BigDecimal(5); BigDecimal spendingOne = new BigDecimal(0.25); BigDecimal spendingTwo = new BigDecimal("0.47"); BigDecimal spendingThree = new BigDecimal ("1.73"); BigDecimal finalBalance = initialBalance.subtract(spendingOne).subtract(spendingTwo).subtract(spendingThree); System.out.println("Final balance after all the spendings: " + finalBalance); assertThat(finalBalance, is(new BigDecimal(2.55))); }
内容的提问来源于stack exchange,提问作者CowOO
相关产品推荐
相关产品推荐

