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 / 1 2 / 1 4 / ⋯ \cdots/8/4/2/1/\frac{1}{2}/\frac{1}{4}/\cdots ⋯/8/4/2/1/21/41/⋯
可以看出来,十进制可以看成权重分别是
⋯ / 1 0 4 / 1 0 3 / 1 0 2 / 1 0 1 / 1 0 0 / 1 10 / 1 1 0 2 / 1 1 0 3 ⋯ \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⋯
二进制权重也就是
⋯ / 2 3 / 2 2 / 2 1 / 2 0 / 1 2 1 / 1 2 2 / ⋯ \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,二进制情况下的说法就是
1 2 1 \frac{1}{2^1} 211
计算机只使用二进制,可以表示的浮点数d也就一目了然了。
集 合 T = [ ⋯ / 2 3 / 2 2 / 2 1 / 2 0 / 1 2 1 / 1 2 2 / ⋯ ] , 则 d = ∑ a i , a i ∈ 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,ai∈T
也就是说可以用计算机表示出来的***浮点数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=a−b,n=c−d,其中(a=c,b=d不同时存在,a、b、c、d都是含有有限位小数的小数)
的情况下,
如 果 理 论 上 m = n , 则 计 算 机 中 m ≠ n 如果理论上m=n,则计算机中m\neq n 如果理论上m=n,则计算机中m=n
想要讲解的讲解完了,其他部分请参考本文章提到的《JAVA开发手册》。里面有解析以及解决方案,另外没事去考一下“阿里巴巴认证证书——阿里巴巴编码规范(Java)”也是挺好的。