踩坑记录,double相加位数变多,使用BigDecimal优化

最近在做账单相关的需求,需要涉及到金额相加的操作,由于项目中原来的金额都是使用的是double,因此在写的时候,同样也使用用了double,结果在测试站的时候,发现有的数据会出现了小数点变得非常多的情况。

一、问题复现

public static void main(String[] args) {
    Double dd =  99.66 +  88.99;
    System.out.println(dd);
}

结果:

188.64999999999998

可见结果并不是我们想要的,我们想要的是188.65

二、原因

double是双精度浮点数类型,它使用IEEE 754标准来表示实数。然而,由于计算机内部表示的是二进制小数,而大多数十进制小数无法精确表示为二进制小数,因此存在精度损失的问题。
yyou踩坑记录,double相加位数变多,使用BigDecimal优化_第1张图片
由上图可知,浮点数数在计算机中的二进制表示,有些浮点数在是不能用计算机准确地用二进制数表示的。
例如:

在计算机中,0.1 和 0.2 的十进制表示在二进制中是无限循环的小数,因此它们的二进制表示是近似值。下面是它们的近似二进制表示:

0.1 的近似二进制表示:
0.00011001100110011001100110011001100110011001100110011…

0.2 的近似二进制表示:
0.00110011001100110011001100110011001100110011001100110…
1.25 的二进制表示:1.01
2.5 的二进制表示:10.1

因此当两个double类型的数相加时,它们的小数部分可能会导致精度损失,从而产生舍入误差。这舍入误差可能导致结果的位数比原始操作数的位数多。

三、解决

1.使用String进行格式化结果(不推荐)
在计算完成之后,对结果进行格式化,保留两位小数,这种情况下可能会导致结果不准确,因为在计算的过程中精度就缺失了,但也不会相差太大。

2.使用BigDecimal

  public static void main(String[] args) {
        BigDecimal a = new BigDecimal("99.66");
        BigDecimal b = new BigDecimal("88.99");
        System.out.println(a.add(b));
    }

结果:

188.65

BigDecimal 类可以解决浮点数精度问题的主要原因在于它使用了任意精度的十进制算法,而不是二进制浮点数表示。以下是使用 BigDecimal 类解决浮点数精度问题的几个关键特点:

  • 任意精度: BigDecimal 可以表示任意精度的十进制数,因为它内部使用了整数数组来表示数值。这使得 BigDecimal 能够维持高精度,而不受二进制浮点数表示的固定精度限制。

  • 精确的小数表示: BigDecimal 对小数的表示非常精确,不会丢失任何位数。这是因为 BigDecimal 内部存储了小数点后的所有位数,不受二进制浮点数的限制。

  • 精确的运算: 在 BigDecimal 中进行加、减、乘、除等运算时,它会保留尽可能多的位数,避免了浮点数运算时的精度损失。这使得在需要高精度计算的场景中,BigDecimal 是一个可靠的选择。

  • 不受二进制浮点数舍入误差影响: BigDecimal 避免了二进制浮点数在表示小数时可能产生的舍入误差,因为它是基于十进制算法的。

四、总结

在计算机中,浮点数精度问题是由于二进制浮点数的表示方式导致的,而有些十进制小数无法精确表示为有限的二进制小数。这导致在浮点数运算中可能出现舍入误差,影响计算结果的精确性。在需要高精度计算的情况下,使用BigDecimal类是一种可靠的选择,能够避免浮点数精度问题带来的困扰。

你可能感兴趣的:(java)