问题
浮点数运算精度丢失代码演示:
float a = 2.0f - 1.9f;
float b = 1.8f - 1.7f;
System.out.println(a); //0.100000024
System.out.println(b); //0.099999905
System.out.println(a==b); //false
为什么会出现这个问题呢?
这个和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。这也就是解释了为什么浮点数没有办法用二进制精确表示。
就比如说十进制下的 0.2 就没办法精确转换成二进制小数:
// 0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止,
// 在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。
0.2 * 2 = 0.4 -> 0
0.4 * 2 = 0.8 -> 0
0.8 * 2 = 1.6 -> 1
0.6 * 2 = 1.2 -> 1
0.2 * 2 = 0.4 -> 0(发生循环)
所以 0.2(D) = 0.00110…(B)。
关于浮点数的更多内容,可以参考计算机系统基础(四)浮点数这篇文章。
解决
我们需要用到BigDecimal类
BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到money的场景)都是通过 BigDecimal 来做的。
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("1.9");
BigDecimal c = new BigDecimal("1.8");
BigDecimal d = new BigDecimal("1.7");
BigDecimal x = a.subtract(b); //a-b
BigDecimal y = c.subtract(d); //c-d
System.out.println(x); //0.1
System.out.println(y); //0.1
System.out.println(x.equals(y)); //true