Java中double转BigDecima导致精度损失

1.楔子

服务中如下的代码出现了诡异的不相等问题? 最后发现是double转bigDecimal时精度损失导致。代码和现象如下:

        @Test
    public void doubleToDecimal() {
        double amountDouble = 16.67;
        BigDecimal amountDecimal = new BigDecimal("16.67");
        System.out.println(amountDecimal.compareTo(new BigDecimal(amountDouble)));

        System.out.println("amountDouble: " + amountDouble);
        System.out.println("amountDecimal: " + amountDecimal);
        System.out.println("new BigDecimal(amountDouble): " + new BigDecimal(amountDouble));
        System.out.println("BigDecimal.valueOf(amountDouble): " + BigDecimal.valueOf(amountDouble));
        System.out.println("new BigDecimal(Double.toString(amountDouble)): " + new BigDecimal(Double.toString(amountDouble)));
        System.out.println("new BigDecimal(String.valueOf(amountDouble)): " + new BigDecimal(String.valueOf(amountDouble)));
    }

在这里插入图片描述
上面 16.67浮点数转为new BigDecimal()时出现了精度损失,可以看到16.67变为16.6700000000000017053025658242404460906982421875,这才导致compareTo不相等。

2.分析和正确使用姿势

float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。正式场合应该使用BigDecimal。

因为十进制的16.67本身就是无法用二进制精确表示的,也就说无论你的精度是多少位,都无法用二进制来精确表示16.67,所以对于double数字只能接近表示,这就是二进制计算机的缺点,就如同十进制也也无法表示1/3,1/6一样。如果用这个double来初始化bigDecimal的话就会出现同样的问题。

BigDecimal的构造函数public BigDecimal(double val)会损失了double 参数的精度。jdk中已经明确不建议使用new BigDecimal(double value)这种形式的构造函数,而是使用new BigDecimal(Stringvalue)BigDecimal.valueof( double value)

new BigDecimal(double value) javadoc如下(已经说的很明确了,会出现不可预测的情况),不推介使用:
在这里插入图片描述

BigDecimal.valueof( double value) 源码如下:
在这里插入图片描述
该方法是推荐的从double转为BigDecimal的方法。
在这里插入图片描述

3.结论

不建议使用new BigDecimal(double value)这种形式的构造函数,而是使用new BigDecimal(Stringvalue)BigDecimal.valueof( double value)

4.扩展阅读

发布了418 篇原创文章 · 获赞 738 · 访问量 125万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览