Java规范Java面试题之浮点数双精度相等问题

本文深入探讨Java中浮点数运算的精度问题,通过具体示例解释为什么看似相等的浮点数在计算机中却不相等。文章揭示了浮点数采用的“尾数+阶码”编码方式如何导致这一现象,并提供了理解和解决该问题的方法。

Java规范Java面试题之浮点数双精度相等问题

ps:简单的大学生应该知道的计算机组成原理。可以直接跳过不看

问题描述

工作空闲,同学群里有开始各种各样结婚生孩子养生的话题。翻阅阿里《JAVA开发手册v1.5.0华山版》,第八页,随手把一个规范的反例发到群里让他们看下结果,demo如下:

    float a = 1.0f - 0.9f;
    float b = 0.9f - 0.8f;
    if (a == b) {
        // 预期进入此代码快,执行其它业务逻辑
        // 但事实上 a==b 的结果为 false
        System.out.println("niubi");
    }
    Float x = Float.valueOf(a);
    Float y = Float.valueOf(b);
    if (x.equals(y)) {
        // 预期进入此代码快,执行其它业务逻辑
        // 但事实上 equals 的结果为 false
        System.out.println("shabi");
    }

群里两个再喊结果是打印“shabi",当然注释部分删除后发群里的。

问题分析

看下阿里《JAVA开发手册v1.5.0华山版》的言简意赅的解释:

说明:浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进
制无法精确表示大部分的十进制小数

Java 开发手册 8/44

同学继续疑问——大部分的小数指那些?

二进制的数表示十进制,都知道类似8421这种权重。

比如十进制的15可以用00001111表示,也就是8+4+2+1;那小数呢

都知道小数0.5=5*10的-1次方,这样说还不明显(因为这种说法是针对十进制的)。那换一种说法(二进制):

刚才说的8421码权重,再往后是什么,我们可以说是
⋯/8/4/2/1/12/14/⋯ \cdots/8/4/2/1/\frac{1}{2}/\frac{1}{4}/\cdots /8/4/2/1/21/41/
可以看出来,十进制可以看成权重分别是
⋯/104/103/102/101/100/110/1102/1103⋯ \cdots/10^4/10^3/10^2/10^1/10^0/\frac{1}{10}/\frac{1}{10^2}/\frac{1}{10^3}\cdots /104/103/102/101/100/101/1021/1031
二进制权重也就是
⋯/23/22/21/20/121/122/⋯ \cdots/2^3/2^2/2^1/2^0/\frac{1}{2^1}/\frac{1}{2^2}/\cdots /23/22/21/20/211/221/
那现在再看小数0.5,二进制情况下的说法就是
121 \frac{1}{2^1} 211
计算机只使用二进制,可以表示的浮点数d也就一目了然了。
集合T=[⋯/23/22/21/20/121/122/⋯ ],则d=∑ai,ai∈T 集合T=[\cdots/2^3/2^2/2^1/2^0/\frac{1}{2^1}/\frac{1}{2^2}/\cdots],则 d=\sum{a_i},a_i\in T T=[/23/22/21/20/211/221/]d=ai,aiT
也就是说可以用计算机表示出来的***浮点数d为集合T中任意N项的和***,另外任意一个数字使用的任意N项绝对不同。原理相当于十进制,你问我为什么10和11不一样。换成二进制也一样,为什么1101和1100不一样?因为都是唯一的,像唯一主键一样。

举个例子:

0.75=0.5+0.25,其他不在一一举例了。

回过头在看原题目:

float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
if (a == b) {
    // 预期进入此代码快,执行其它业务逻辑
    // 但事实上 a==b 的结果为 false
}

理论上相等的a和b,在计算机中为什么不相等。如果换成如下代码,是否相等?

float a = 0.1f;
float b = 0.1f;
if (a == b) {
    
}

很显然,是相等的。因为都是0.1f,数字本身就可以说只具有唯一性,在某一进制中与其他数字的区别具有唯一性。

回到原题,0.9和0.8是一定不能用计算机精确表示出来的。都是用近似数来表示,其实上面我已经解释的很清楚了,看个人理解,就知道1.0f-0.9f一定不和0.9f-0.8f相等。

不止如此,同理:
m=a−b,n=c−d,其中(a=c,b=d不同时存在,a、b、c、d都是含有有限位小数的小数) m=a-b,n=c-d,其中(a=c,b=d不同时存在,a、b、c、d都是含有有限位小数的小数) m=ab,n=cd,a=c,b=dabcd
的情况下,
如果理论上m=n,则计算机中m≠n 如果理论上m=n,则计算机中m\neq n m=n,m=n
想要讲解的讲解完了,其他部分请参考本文章提到的《JAVA开发手册》。里面有解析以及解决方案,另外没事去考一下“阿里巴巴认证证书——阿里巴巴编码规范(Java)”也是挺好的。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值