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

 前言

在现实中我们都知道:

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)的, 在进行每一次四则运算时,都会产生一个新的对象 ,所以在做加减乘除运算时要记得要保存操作后的值。

posted @ 2023-11-29 15:41  echosada  阅读(397)  评论(0)    收藏  举报