解决java中0.1+0.2=0.30000000000000004的问题

发布时间 2023-11-13 20:11:34作者: echosada

 前言

在现实中我们都知道:

0.1+0.2=0.3

但是在程序中会出现这样的结果:

0.1+0.2=0.30000000000000004

原因

对于0.1来说,其本质是 1 / 10,那么若你用二进制表示它们,然后除的话,是这样的:1 / 1010,然而这一个是除不尽的,是无穷循环。

 ===> 0.0 00110011001100110011001100110011... 其中0011循环

而0.2表示为0.0011001100110011...

而在二进制中 1 + 1 = 10,所以 0.1 + 0.2 = 0.0100110011001100...

转成10进制就近似表示为 0.30000000000000004

结论

这是由于计算机采用二进制存储浮点数,而二进制无法准确地表示十进制小数0.1和0.2,会发生精度误差。在计算0.1+0.2时,计算机实际上是计算这两个数的近似值的和,因此得到的结果也是一个近似值,其末尾的小数位可能会有误差。

解决方法

使用Java中的BigDecimal类。

Java中的基本数据类型(如double和float)是有限的,因此它们对于小数计算可能会产生误差。在需要高精度计算时,可以使用Java中的BigDecimal类。

BigDecimal类可以处理大量的小数位数,避免了在浮点数位数过多时出现的不精确问题。以下情况可能需要使用BigDecimal类:

  1. 当需要精确计算小数,需要避免由于精度丢失而导致错误结果时;
  2. 当需要进行货币计算时,例如金融应用程序;
  3. 当需要进行科学计算时,需要高精度的数据存储。

BigDecimal 常用的构造方法如下。

BigDecimal(String val)用一个字符串值创建一个BigDecimal对象。
BigDecimal(double val)用一个double值创建一个BigDecimal对象。
BigDecimal(long val)用一个long值创建一个BigDecimal对象。
BigDecimal(BigInteger val)用一个BigInteger值创建一个BigDecimal对象。

参数类型为double的构造方法的结果有一定的不可预知性,因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。

而参数类型为String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言, 通常建议优先使用String构造方法。

下面列出了 BigDecimal 类用于实现加、减、乘和除运算的方法。

add(BigDecimal val)将当前BigDecimal对象与另一个BigDecimal对象相加。
subtract(BigDecimal val)将当前BigDecimal对象与另一个BigDecimal对象相减。
multiply(BigDecimal val)将当前BigDecimal对象与另一个BigDecimal对象相乘。
divide(BigDecimal val)将当前BigDecimal对象与另一个BigDecimal对象相除。

除此之外BigDecimal 类的其他方法。

compareTo(BigDecimal val)比较当前BigDecimal对象与另一个BigDecimal对象的大小。
negate()将当前BigDecimal对象取相反数。
abs()将当前BigDecimal对象取绝对值。
round(MathContext mc)将当前BigDecimal对象按照指定的精度进行四舍五入。
intValue()将当前BigDecimal对象转换为int类型。
doubleValue()将当前BigDecimal对象转换为double类型。
toString()将当前BigDecimal对象转换为字符串。

测试代码: 

  1. public class Main {
  2. public static void main(String[] args) {
  3. BigDecimal a = new BigDecimal("0.1");
  4. BigDecimal b = new BigDecimal("0.2");
  5. BigDecimal c = a.add(b);
  6. System.out.println(c);
  7. }
  8. }

 运行结果:

 总结

1、在需要精确的小数计算时再使用BigDecimal,BigDecimal的性能比double和float差,在处理庞大,复杂的运算时尤为明显。故一般精度的计算没必要使用BigDecimal。
2、尽量使用参数类型为String的构造函数。
3、BigDecimal都是不可变的(immutable)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。