专栏名称: Java专栏
一个Java、Python、数据库、中间件、业内资讯、面试、学习资源等干货的知识分享社区。
目录
相关文章推荐
幸福东台  ·  考前提醒 ·  16 小时前  
度房苏州  ·  公积金新政!事关灵活就业人员! ·  昨天  
51好读  ›  专栏  ›  Java专栏

长点心吧!别再用"=="比较浮点数了

Java专栏  · 公众号  ·  · 2021-03-20 12:20

正文


超全面!Java核心知识总结(点击查看)

超全面!Java核心知识总结(点击查看)


大家好,我是胖虎。前几天群内有个小伙伴去面试,在做笔试题的时候在这里掉坑里了。

float a1 = 0.1f;
double a2 = 0.1;
System.out.println((a1 - a2) == 0.0);

float b1 = 0.125f;
double b2 = 0.125;
System.out.println((b1 - b2) == 0.0);

double c1 = 0.625;
double c2 = 0.5;
double c3 = 0.375;
System.out.println((c1 - c2) == (c2 - c3));

double d1 = 0.8;
double d2 = 0.6;
double d3 = 0.4;
System.out.println((d1 - d2) == (d2 - d3));

大家可以先考虑一下最终的输出结果是怎么样的?


正确的答案为

false
true
true
false


这是因为在处理浮点数的时候如果操作不当很容易造成精度丢失,我们可以模拟一个发红包的场景。

一个红包中有0.5元,4个人每人从里面拿走了0.1元。

不要问我为啥钱这么少,因为我...

用代码来输出还剩多少钱。。。

double d1 = .5;
for (int i = 0; i 4; i++) {
 d1 -= .1;
}
// 输出余额
System.out.println(d1);

非常简单的一个减法运算,但是执行之后却发现,输出的结果并不是 0.1 ,而是 0.10000000000000003

这就是我们常见的精度丢失问题,并不是Java语言才有这种的问题,很多语言都会遇到这样的问题。这是因为他们都遵循了 IEEE 754 标准,即: IEEE二进制浮点数算术标准(IEEE 754)

编码方式是符号+阶码+尾数,如图:

img

比如 float 类型来具体,我们都知道它占用 32 位,单精度浮点表示法如下:

  • 符号位(sign)占用 1 位,用来表示正负数,0 表示正数,1 表示负数
  • 指数位(exponent)占用 8 位,用来表示指数,实际要加上偏移量
  • 小数位(fraction)占用 23 位,用来表示小数,不足位数补 0

由此可以看出,指数位决定了代销范围,小数位决定了它的精度。当在计算机中进行计算时,十进制转化为二进制表达式后,得到的尾数会很长很长。

而实际显示的尾数十倍截取或执行舍入后的近似值。这样就解释清楚了浮点数计算不准确的问题。

同理由于上述原因的存在,在对浮点数进行比较的时候非常不建议使用 == ,因为精度问题,往往会返回不符合与其的结果。当然也不能使用包装类型的 equals

double d1 = .1 * 3;
double d2 = .3;
System.out.println(d1 == d2);

上面的例子中,按照正常的逻辑来说 .1 * 3 结果是 0.3 。所以最终的结果应该是 True

然而执行之后结果确实 False ,d1最终的值并不是 0.3 而是 0.30000000000000004

问题的原因我们已经知道了,那遇到浮点数的时候该 如何正确的进行比较呢?

常用的方法有两种

1、规定误差区间

虽然我们无法做到精确的比较,但是我们可以确定一个误差范围,只要小于这个误差范围,我们就可以它们是相等的。

final double threshold = .00001;
double d1 = .1 * 3;
double d2 = .3;
if (Math.abs(d1 - d2)     System.out.println("d1 = d2");
else {
    System.out.println("d1 != d2");
}

通过设定一个阈值 threshold ,然后对两个变量求绝对值,如果差值小于这个阈值,则默认相等.







请到「今天看啥」查看全文