BigDecimal计算的这些坑,让我的程序产生难以想象的Bug

9 篇文章 1 订阅

通常我们在金融、科学等场景,会使用BigDecimal。然而如果我们不注意BigDecimal的精度问题,计算结果偏差可能会很大,最终会产生难以想象的Bug。

1意想不到的加减乘除

@Test
public void test3() {
    BigDecimal param1 = new BigDecimal(0.1);
    BigDecimal param2 = new BigDecimal(0.2);
    //加法
    BigDecimal add = param1.add(param2);
    System.out.println("加法:" + add);

    //减法
    BigDecimal subtract = param2.subtract(param1);
    System.out.println("减法:" + subtract);

    //乘法
    BigDecimal multiply = param1.multiply(param2);
    System.out.println("乘法:" + multiply);

    //除法
    BigDecimal divide = param2.divide(param1);
    System.out.println("除法:" + divide);
}

运行结果

程序的结果并不是我们想要的结果。这里实际是将数值做了二进制转换存在计算机中,取出时又转成了10进制,导致值产生了变化。

 

改进方法如下:

我们将

BigDecimal param1 = new BigDecimal(0.1);
BigDecimal param2 = new BigDecimal(0.2);

更改为

BigDecimal param1 = new BigDecimal("0.1");
BigDecimal param2 = new BigDecimal("0.2");

运行结果

所以我们在使用 BigDecimal 表示和计算浮点数时,一定要使用字符串的构造方法来初始化 BigDecimal

 

2除法运算记得保留小数点位数

@Test
public void test5() {
    BigDecimal bParam = new BigDecimal("1");
    BigDecimal bParam2 = new BigDecimal("3");
    BigDecimal divide = bParam.divide(bParam2);
    System.out.println(divide);
}

如上代码我们1除以3时结果会是无线循环小数,运行结果如下:

直接抛出异常了。为此,在使用BigDecimal做除法运算时,我们记得保留小数点位数。

 

@Test
public void test5() {
    BigDecimal bParam = new BigDecimal("1");
    BigDecimal bParam2 = new BigDecimal("3");
    BigDecimal divide = bParam.divide(bParam2, 3, BigDecimal.ROUND_HALF_EVEN);
    System.out.println(divide);
}

运行结果为:

3烦人的小数点

如果你有一个Double类型的数字,还有一个BigDecimal类型的数字,现在我要对这两个数字做乘法:

Double dParam = 100d;
BigDecimal bParam = new BigDecimal("0.02");
BigDecimal toBigDecimal = new BigDecimal(dParam.toString());
BigDecimal multiply = bParam.multiply(toBigDecimal);

如上代码,我将Double类型的100dBigDecimal类型的0.02相乘,我期望得到值为2.00。我按照上面的方法做乘法后,得到的数为

小数点后面居然有3个0,想当然不是我想要的。

 

实际上我们只要做做小改动:

@Test
public void test4() {
    Double dParam = 100d;
    BigDecimal bParam = new BigDecimal("0.02");
    BigDecimal toBigDecimal = new BigDecimal(dParam.toString());
    BigDecimal multiply = bParam.multiply(toBigDecimal);
    System.out.println(multiply.setScale(2, BigDecimal.ROUND_HALF_EVEN));
}

运算结果:

大部分业务需求会保留小数位数,我们可以在结果中设置小数位数即可。

4别用equals判断

我们在对包装类进行比较时,通常使用equals比较值,但是在BigDecimal中使用equals可能就不会得到我们想要的结果。

@Test
public void test6() {
    System.out.println(new BigDecimal("1.0").equals(new BigDecimal("1")));
}

返回结果为:

扒开这里的equals源码我们发现equals比较的是BigDecimalvaluescale,即比较了值和小数点位数。我们往往只需要比较值,那么这里可以使用compareTo方法:

 

@Test
public void test6() {
    System.out.println(new BigDecimal("1.0").compareTo(new BigDecimal("1")));
}

运行结果:

0代表的就是相等了。

这篇文章就写到这里了,如果对你有用,欢迎转发。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值