关于BigDecimal的坑

我们都知道如果要使用Java进行浮点数运算是不能直接使用+-*/运算符去直接运算的,否则会丢失经度,类似这样的:

1
2
double d = 0.1 + 0.2;
System.out.println(d);

输出的结果就是0.30000000000000004,这个结果明显是有问题的。

要解决精度丢失的问题,一般我们会用到BigDecimal类,但是这个类其实也是有坑的。

  • 坑1:如果使用构造方法创建BigDecimal,则参数必须为String类型的,否则还是会丢失精度,比如:
1
2
3
4
5
BigDecimal bd2 = new BigDecimal("0.1");
BigDecimal bd3 = new BigDecimal(0.1);
System.out.println(bd2);
System.out.println(bd3);

运行的结果就是:

1
2
0.1
0.1000000000000000055511151231257827021181583404541015625

所以,如果要用构造方法去创建BigDecimal对象,则参数一定要是String类型的!

  • 坑2:另一种创建BigDecimal对象的方法是使用BigDecimal类的valueOf()方法,问题是这个方法也有坑,如果你给的参数是long,double是没有问题的,如下:

    1
    2
    BigDecimal bd1 = BigDecimal.valueOf(0.1);
    System.out.println(bd1);

    输出0.1,如果传给valueOf()一个float类型的参数会怎样呢?

    1
    2
    BigDecimal bd2 = BigDecimal.valueOf(0.1f);
    System.out.println(bd2);

    输出0.10000000149011612,发现问题了吧,出现了精度丢失的问题。关于为什么出现这个问题,可以看看valueOf()方法的源码

    1
    2
    3
    4
    5
    6
    7
    public static BigDecimal valueOf(double val) {
    // Reminder: a zero double returns '0.0', so we cannot fastpath
    // to use the constant ZERO. This might be important enough to
    // justify a factory approach, a cache, or a few private
    // constants, later.
    return new BigDecimal(Double.toString(val));
    }

    底层是调用了构造方法,参数是用Double类的toString()方法把我们传进去的参数转换成了字符串,如果我们直接传递double类型的是没问题的,如果传递的是float类型的则会自动转换成double类型,类似

    1
    2
    double d = 0.1f;
    System.out.println(d);

    输出0.10000000149011612,看,丢失精度了吧。

所以,总结上面,如果使用BigDecimal类,则创建对象的时候如果使用构造方法,则参数一定要是String,就算不是,也要转换成String,如果使用valueOf()方法创建对象,则参数必须是long,double这种的。

如果觉得本文对你有帮助,请支持我!