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)”也是挺好的。