Java 浮点数运算不准确的原因及解决方案

前言

问题

执行如下代码,你觉得会打印出什么结果?

        System.out.println(0.1 + 0.2);
        System.out.println(0.2 - 0.1);
        System.out.println(0.1 * 0.2);
        System.out.println(0.2 / 0.1);
        
        System.out.println(0.3 - 0.1);
        System.out.println(0.3 / 0.1);

预期

0.3
0.1
0.02
2.0
0.2
3.0

我相信绝大多数人会说出如上结果。事实呢?

事实

Java 浮点数运算不准确的原因及解决方案_第1张图片
你认为你看错了,但结果却是是这样的。问题在哪里呢?

为什么?

计算机是二进制的。浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。

怎么办?

其实java的float只能用来进行科学计算或工程计算,在大多数的商业计算中,一般采用java.math.BigDecimal类来进行精确计算。

BigDecimal

 public BigDecimal(double val) //将double表示形式转换为BigDecimal (不建议使用)
 public BigDecimal(int val)//将int表示形式转换成BigDecimal
 public BigDecimal(String val)//将String表示形式转换成BigDecimal

为什么不建议double型的参数构造方法呢?

Java 浮点数运算不准确的原因及解决方案_第2张图片

  1. 参数类型为double的构造方法的结果有一定的不可预知性。因为计算机无法准确的表示浮点数,反之亦然,我们的double型0.1对于计算机来说并不是,对于计算机来说它认为的0.1其实是0.1000000000000000055511151231257827021181583404541015625。
  2. String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,通常建议优先使用String构造方法,当double必须用作BigDecimal的源时,使用Double.toString(double)转成String。

使用

public BigDecimal add(BigDecimal value);//加法

public BigDecimal subtract(BigDecimal value);//减法 

public BigDecimal multiply(BigDecimal value);//乘法

public BigDecimal divide(BigDecimal value);//除法

注意

整除问题

BigDecimal除法可能出现不能整除的情况,比如 4.5/1.3,这时会报错

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

其实divide的重载方法有最多可以传三个参数

 public BigDecimal divide(BigDecimal divisor)
public BigDecimal divide(BigDecimal divisor, int roundingMode)
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) 
public BigDecimal divide(BigDecimal divisor, RoundingMode roundingMode)
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)

第一参数表示除数。
第二个参数表示小数点后保留位数。
第三个参数表示舍入模式,只有在作除法运算或四舍五入时才用到舍入模式。

新对象

BigDecimal在进行每一步运算时,都会产生一个新的对象。原对象不变。

参考

https://www.cnblogs.com/LeoBoy/p/6056394.html

你可能感兴趣的:(Java笔记)