什么?使用BigDecimal计算也会出现精度问题

内容分享20小时前发布
0 6 0

一般金融系统计算涉及到小数的,我们都会使用BigDecimal来完成加减乘除运算。但是利用BigDecimal进行浮点数准确运算时,需要注意使用正确的方法。如果方法选择不当,依旧会发生错误。

问题起因

什么?使用BigDecimal计算也会出现精度问题

a、测试BigDecimal的两种构造方法,发现浮点运算的结果不同。利用BigDecimal.valueOf 方法构造对象的方法,获得的浮点数发生了精度异常。

b、利用 new BigDecimal(String val) 方法,运算正确。

分析问题

查看BigDecimal.valueOf源码,我们可以发现问题。BigDecimal.valueOf(9.9f)实际调用的方法是java.math.BigDecimal#valueOf(double)。如下面源码所示:

    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));
    }

比我们想象的多了一步强制类型转换的动作。9.9f会被强制转换成double类型。测试这个强制转换,我们可以发现问题,实则出在强制转换这里。强制转换结果如下。

double v = 9.9f;
System.out.println(v);
double v2 = Double.valueOf(9.9f);
System.out.println(v2);

输出
9.899999618530273
9.899999618530273

拓展

如果参数是double类型,我们使用 BigDecimal.valueOf(…) 可以吗?

BigDecimal multiply3 = BigDecimal.valueOf(9.9d).multiply(BigDecimal.valueOf(100d));
System.out.println(multiply3.toString());

输出
990.00

看结果是没问题的。但是,注意 BigDecimal.valueOf(…) 的源码,其内部依然是把入参转换了 double 数据对应的字符串。如果直接使用 new BigDecimal(double val) 构造函数来进行运算,则会发现计算结果发生来精度异常。如下所示。

BigDecimal multiply4 = new BigDecimal(9.9d).multiply(BigDecimal.valueOf(100d));
System.out.println(multiply4.toString());

输出
990.00000000000003552713678800500929355621337890625000

可以看到jdk内部的注释,已经说明,new BigDecimal(double val)方法得到的结果是不可预知的。推荐使用入参类型为String的构造函数来进行浮点数的准确计算。

结尾总结

在使用BigDecimal进行科学运算的时候,我们需要清楚,BigDecimal.valueOf(…) 和 new BigDecimal(…) 的使用效果不同。当 BigDecimal.valueOf(…)的入参是float类型时,BigDecimal 会把入参强制转换成double类型。

使用new BigDecimal(String val)来获得浮点数对应的 BigDecimal 对象,进而完成浮点数准确运算。

什么?使用BigDecimal计算也会出现精度问题

BigDecimal

© 版权声明

相关文章

6 条评论

  • 头像
    念儿 读者

    我是被图骗进来的~不过既然你用了BigDecimal,那计算过程中、哪怕是最后结果输出都得永BigDecimal保持一致,可别混个double进去~

    无记录
    回复
  • 头像
    冰冷热带鱼鱼o 投稿者

    是的 正常开发中不会遇到 文章列的是一种极端情况

    无记录
    回复
  • 头像
    抵达恐惧边缘 投稿者

    bigdecimal不要用double构造

    无记录
    回复
  • 头像
    开发商在线 读者

    是的 注意这个就不会出问题了

    无记录
    回复
  • 头像
    中山天气 投稿者

    浮点数只有指定精度才能保证计算结果符合预期

    无记录
    回复
  • 头像
    ZETA_Alphar 投稿者

    是的 但文中这种很容易让人忽视 比如9.9*100这种 习惯性的以为会得到990

    无记录
    回复