BigDecimal的使用问题

guardyou3
2023-10-11 / 6 评论 / 71 阅读 / 正在检测是否收录...

关于使用BigDecimal会出现的几个常见问题

介绍 :BigDecimal类在金融开发行业被广泛使用,不知道大家在使用BigDecimal的时候是否会出现一些问题,这里博主来介绍使用BigDecimal可能会导致的错误( 文章完整示例代码在底部

demo1 :

lnkj23kj.png
这里我们创建两个BigDecimal对象,使两个对象的值都为0.1,第一个bigDecimal1直接传入一个浮点数,第二个bigDecimal2通过valueOf的方式创建对象,那个如果我们运行这个测试,那么按照我们最初的想法,打印的值都为0.1才对,但是事实真是如此吗?下面我们来进行测试。

demo1结果:

lnkjczhr.png

我们观察发现,第一个bigDecimal1的值为一长串的浮点数,它其实是一个0.1的近似值,而BigDecimal2的值为0.1,具体的原因其实是因为0.1无法被一个有限位的二进制表示出来,所以是一个0.1的近似值存在了BigDecimal中,而第二个值bigDecimal2是一个准确值,为什么呢?我们通过其源码来进行分析。

lnkjhrlp.png
我们可以发现在其源码中,返回的BigDecimal对象经过了Double的数值转换为一个字符串,此时不会出现精度问题

注意:如果我们传入的值超过了double的范围,他也会出现精度问题

解决方法: 既然我们知道valueOf在底层帮我们进行了一个字符串转换,那么我们直接在创建BigDecimal对象的时候就直接传入一个字符串,那么我们不是就可以绕过这个问题了吗。
例子:

lnkjx0yp.png
我们任意传入一个很大的值,可以发现并没有出现精度问题,因此我们就可以通过传入字符串的方式避免第一个陷阱。其实idea已经给了我们提示了:

lnkjzbqy.png


demo2:

lnkk8lw8.png
这里我们创建两个BigDecimal对象,我们对其分别使用equals方法和compareTo方法进行值的比较

demo2结果:

lnkkbbyk.png

我们可以发现,通过equals方法进行比较的值是false,而compareTo方法返回0(true),为什么第一个方法的比较值为false呢?我们进入源码中查看

问题分析:

lnkkfhdp.png

通过观察发现,BigDecimal对象重写了Object类的equals方法,在此方法中首先对其 精度进行了比较 ,这也就是说如果两个对象的值在逻辑上是相同的,但因为精度不同的BigDecimal对象将不被认为其值相等,这就是为什么会返回false原因,而compareTo方法只比较数值,返回值为-1,0,1。

因此我们如果要对两个BigDecimal对象进行数值比较的话,采用compareTo方法返回0即可说明相等,当然如果在某些情况需要强行让精度相同才认为数值相同的话,我们也可以使用equals方法进行比较,灵活运用就好



demo3:

lnkktp2j.png

我们定义了两个BigDecimal对象,值分别为1.0和3.0,我们让两个对象相除,这个时候是否会有问题?我们知道1除以3的结果是一个循环小数0.3333...,如果我们执行程序能够正常打印吗?

demo3结果:
lnkkwmfa.png
发现此时抛出了一个异常,其大致的意思就是没有指定精度,返回的结果必须是一个精确的数值。

问题解决: 其实在BigDecimal已经帮我们解决了这个问题了

lnkl4jy3.png
我们可以使用divide的一个重载方法,其第一个参数是divisor是除数,第二个参数scale是精度,也就是返回的小数位数,第三个参数是RoundingMode是一个枚举类,我们可以通过其指定保留精度的规则,比如四舍五入或者直接抹掉。
修改后的代码:

lnkla5xv.png

此时我们指定保留两位小数,并且采用四舍五入的方式(RoundingMode.HALF_UP),发现正确打印出来精确的结果0.33


综上就是使用BigDecimal可能会出现的三种问题
demo源码:

public class BigDecimalTest {
    @Test
    public void bigDecimalTest1()
    {
        BigDecimal bigDecimal1 = new BigDecimal(0.1);
        BigDecimal bigDecimal2 = BigDecimal.valueOf(0.1);
        System.out.println("bigDecimal1 = "+bigDecimal1);
        System.out.println("bigDecimal2 = "+bigDecimal2);
        System.out.println(new BigDecimal("11111111111111111111111111111.222222222"));
    }

    @Test
    public void bigDecimalTest2()
    {
        BigDecimal bigDecimal1 = new BigDecimal("0.10");
        BigDecimal bigDecimal2 = new BigDecimal("0.1");
        System.out.println("equals方法比较结果:"+bigDecimal1.equals(bigDecimal2));
        System.out.println("compareTo方法比较结果:"+bigDecimal1.compareTo(bigDecimal2));
    }

    @Test
    public void bigDecimalTest3()
    {
        BigDecimal bigDecimal1 = new BigDecimal("1.0");
        BigDecimal bigDecimal2 = new BigDecimal("3.0");
        BigDecimal bigDecimal3 = bigDecimal1.divide(bigDecimal2,2, RoundingMode.HALF_UP);
        System.out.println("两个数相除的结果:"+bigDecimal3);
    }
}
1

评论 (6)

取消
  1. 头像
    1
    Windows 10 · Google Chrome

    画图

    回复
  2. 头像
    1
    Windows 10 · Google Chrome

    555

    回复
  3. 头像
    1
    Windows 10 · Google Chrome

    555

    回复
  4. 头像
    1
    Windows 10 · Google Chrome

    555

    回复
  5. 头像
    1
    Windows 10 · Google Chrome

    555

    回复
  6. 头像
    1
    Windows 10 · Google Chrome

    555

    回复