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