前言
今天不说废话,直接上代码:
public class Main {
public static void main(String[] args) {
float f1 = 2.0f;
float f2 = 1.1f;
System.out.println("f1 - f2 = " + (double)(f1 - f2));
System.out.println("f1 - f2 == 0.9 ? " + (f1 - f2 == 0.9));
double d1 = 2.0;
double d2 = 1.1;
System.out.println("d1 - d2 = " + (d1 - d2));
System.out.println("d1 - d2 == 0.9 ? " + (d1 - d2 == 0.9));
Float F1 = 2.0f;
Float F2 = 1.1f;
System.out.println("F1 - F2 = " + (F1 - F2));
System.out.println("F1 - F2 == 0.9 ? " + (F1 - F2 == 0.9));
Double D1 = 2.0;
Double D2 = 1.1;
System.out.println("D1 - D2 = " + (D1 - D2));
System.out.println("D1 - D2 == 0.9 ? " + (D1 - D2 == 0.9));
}
}
这段代码分别使用了浮点型和双精度浮点型来计算2-1.1,来看看他的结果是多少,是不是等于0.9呢?让我们看看运行结果:
是不是很惊讶,2 - 1.1 != 0.9
?
计算机中的数据精度问题
要搞明白上面的问题,首先我们要明白这些数据在计算机中是怎么存储的。学过计算机基础的人都知道,计算机只认二进制,我们所有的代码最终都是以二进制的形式在计算机中运行的。计算机在进行运算时,也会将我们的数据转换成二进制来进行运算。
既然要转换成二进制进行运算,那么有些小数便是不能够完全精确的,就好比在10进制中,无法精确表达1/3,同样,在二进制中,无法准确表达1/10,1/10转换成二进制就变成了0.0001100110011001100110011001100110011001100110011001101……
,计算机在进行运算的时候,不可能精确地去计算1/10的值,只能将其截断,那么这样,就出现了所谓的精度丢失问题。那么,有没有一种方案,能够解决这种精度丢失问题呢?
BigDecimal
使用BigDecimal进行2.0-1.1
运算
最常用的方法就是使用BigDecimal,我们将要操作的浮点通过BigDecimal的构造方法传入字符串或使用valueOf方法传入double类型的操作数转换成BigDecimal对象,再通过BigDecimal内置的运算方法进行运算。
public class Main {
public static void main(String[] args) {
BigDecimal b1 = BigDecimal.valueOf(2.0);
BigDecimal b2 = BigDecimal.valueOf(1.1);
System.out.println(b1.subtract(b2).doubleValue());
System.out.println(0.9 == b1.subtract(b2).doubleValue());
}
}
我们来看看运行结果:
我们看到,再使用了BigDecimal进行运算后,成功的达到了我们想要的结果。
BigDecimal提高精度原理
我们可能会有一个疑问,为什么使用BigDecimal不会有精度丢失问题呢?难道他不是使用二进制进行计算的吗?
其实不然,计算机中的所有运算都是通过二进制进行的,BigDecimal也一样。不过与传统运算方式不同的是,BigDecimal在进行小数运算时,会将运算数进行放大操作,使其变成一个整数,再在整数的操作维度上进行运算,这样就避免了精度丢失的问题了。
BigDecimal常用方法
valueOf方法
在上面的代码中,我们就使用valueOf方法实例化了两个BigDecimal类,valueOf可以接收一个数字,并返回一个BigDecimal实例。
BigDecimal() 构造方法
我们也可以通过使用BigDecimal的构造方法直接进行实例化,这里我们可以传入一个数字或者一个字符串进行实例化,同样能够返回一个BigDecimal实例。不过需要注意的是,如果直接传入数字的话,仍然可能会造成精度丢失的问题,因此我们在使用构造方法实例对象是,应当传入一段字符串
add、subtract、multiply、divide
- add方法,进行加法运算,加上传入的BigDecimal对象。
- subtract方法,进行减法运算,减去传入的BigDecimal对象。
- multiply方法,进行乘法运算,乘以传入的BigDecimal对象。
- divide方法,进行除法运算,除以传入的BigDecimal对象。
在上面四种方法,最终都会返回一个新的BigDecimal对象,这个对象就是运算的结果。
toString、doubleValue、floatValue、longValue、intValue
- toString方法,将BigDecimal对象的值转换成一个字符串对象并返回。
- doubleValue方法,将BigDecimal对象的值转换成一个双精度浮点型数并返回。
- floatValue方法,将BigDecimal对象的值转换成一个单精度浮点型数并返回。
- longValue方法,将BigDecimal对象的值转换成一个长整数并返回。
- intValue方法,将BigDecimal对象的值转换成一个整数并返回。
compareTo方法比较
我们可以使用compareTo方法对两个BigDecimal对象进行比较;int a = bigdemical.compareTo(bigdemical2)
若:
- a = -1,表示bigdemical小于bigdemical2;
- a = 0,表示bigdemical等于bigdemical2;
- a = 1,表示bigdemical大于bigdemical2;
BigDecimal常见异常
在我们使用BigDecimal进行除法运算时需要注意,若运算无法整数,出现无限循环小数时,便会抛出ArithmeticException异常。
因此,我们可以在使用divide时,设置结果精确的小数点,例如:
int a = bigdemical.divide(bigdemical2,2)
,此时,在进行bigdemical/bigdemical2时,就会只保留两位小数。