看下面一段程式
#include <stdio.h>int main(){double i;for(i = 0; i != 10; i += 0.1) printf("%.1lf\n", i);}
你也許覺得這個程式很容易理解;但是請別相信自己的想象力,運行一下:
什麼無限輸出下去??
不是按到底到10的時候會停止嗎?
再看一段程式:
#include <cstdio>#define exp 0.000001int main(){if(0.1+0.1+0.1 == 0.3) printf("equal\n"); else printf("not equal\n"); return 0;}
你覺得應該輸出的是:equal
但是運行下:什嗎? not equal???
好吧,我們來討論下;
首先,我們來明白一個概念:
實際上,電腦中的所有資料都用二進位表示,包括浮點數。這就導致某些浮點數不能用二進位精確表示,例如0.1(這很容易理解,就像10/3不能被十進位精確表示一樣)
進一步地說,浮點數是採用分數+指數來表示的,例如
0.5 = 1/2
0.75 = 1/2 + 1/(2^2)
0.875 = 1/2 + 1/(2^2) + 1/(2^3)
0.1 = 1/(2^4) + 1/(2^5) + 1/(2^8) + ...
其中0.1隻能無限迴圈下去,這就意味著0.1在電腦中不能被精確表示,因此產生上述兩個陷阱也就很容易理解;
那麼,我們來怎麼處理這個問題? 首先我們一定不能用 ==
然後;
拿第一個例子為例,你是不是會想到fabs(A-B) < epsilon 這樣的一種判別方法 ;
這個方法在一定情況下是可行的;
但是有一種情況卻會出問題;比如,你的 epsilon 取值為 0.00001 那麼我比較的資料是0.00005和0.00006也會判斷為相等;
這個epsilon實際上是我們常說的絕對誤差;
為了避免這個絕對誤差,我們會想到用相對誤差;
而更精確的結果判斷相等,參見下列函數:
bool IsEqual(float a, float b, float absError, float relError ) { if(a==b) return true; if(fabs(a-b)<absError ) return true; if(fabs(a>b)) return fabs((a-b)/a>relError) ? true : false; if(fabs(a<b))return fabs((a-b)/b>relError) ? true : false; }