浮点类型数值的精度问题产生的原因:

  • 首先,任何数据在内存中都是以二进制的形式存储的(0和1)
  • 浮点类型数值存储在内存前,也需要将十进制的数据转换为二进制(底层实现)
  • 然而,由十进制数值转换成二进制数值也是有一定的条件
    • 1)任意十进制的整数都可以转换成有限位数的二进制整数(分解为2^n组合,其中 n >= 0)
      • 如 171 (2^0 + 2^1 + 2^3 + 2^5 + 2^7) 转换为二进制整数10101011
    • 2)十进制的小数能够分解为 (1/2)^n 时,才能转换成有限位数的二进制小数
      • 如 0.9375 (1/2 + 1/4 + 1/8 + 1/ 16) 就能够转换成二进制小数:0.1111
  • 因此,当无法满足转换规则,出现无限分解的情况时,就需要采用截断的形式
    • 如 0.1 (1/16 + 1/32 + 1/256 + 1/512 + …) 无法完全分解为(1/2)^n时,会无限循环下去,不断地逼近 0.1 这个数值
  • 此时,浮点数的精度问题也就产生了

    BigDecimal类的解决方式

  • 将原来的浮点类型数值,扩大10^n,以整数的形式进行计算操作(BigInteger)

  • 并记录小数点位置(scale)
    • 注意:scale的值是根据两个待计算值的小数点决定的
  • 计算完成后,根据小数点的位置,再除以10^n,得到计算结果
    1. BigDecimal num1 = new BigDecimal("101.101");
    2. // num.scale() 的值为3,表示有三个小数
    3. // num.unscaledValue() 的值为101101,将原来十进制数值扩大了10^3
    4. BigDecimal num2 = new BigDecimal("201.12");
    5. BigDecimal result = num1.add(num2); // 结果为302.221
    6. BigDecimal result2 = num1.multiply(num2); // 结果为20333.43312

参考