說到數據精度,不得不說的,就是數據長度。
這里聊了一下float這個類型,float占4個字節,共4*8=32位。下圖是float在內存格式:
31:標志位,0表示正數,1表示負數。30-23:指數位,共8位。22-0:小數位,共23位。
示例:12345 = 0011 0000 0011 1001 = 1.1 0000 0011 1001 * 2^13
13為指數,存放到指數位。指數可正可負,所以這8位需要表示-127至128。本例中13保存為13+127 = 140 = 1000 1100。
1.1 0000 0011 1001存放在小數位,由於小數點前永遠都是1,所以可以省略,只存1 0000 0011 1001,不足23位,后面補0,即保存為1 0000 0011 1001 0000 0000 00。
最終保存結果:0 1000 1100 1 0000 0011 1001 0000 0000 00
回過頭來,再看12345字個數,二進制為1.1 0000 0011 1001 * 2^13,如果小數部份長度超過23位,就無法完整保存了,只能舍棄多余的部份。
比如1.1 0000 0011 1001 0000 0000 001*2^24,藍色長度為23,實際保存時,只有藍色部份會保存。
那么這個數,就與1.1 0000 0011 1001 0000 0000 000*2^24在內存中是一樣的,但我們知道,這兩個數肯定是不一樣的,精度丟失!
按上述結論,float在整數范圍內,精度能完全保存的最大值是1.1111 1111 1111 1111 1111 111*2^23,即1111 1111 1111 1111 1111 1111 = 16777215。
這個數+1,就成了1 0000 0000 0000 0000 0000 0000 = 1.0000 0000 0000 0000 0000 0000*2^24 = 167772156。
再+1,就成了1 0000 0000 0000 0000 0000 0001 = 1.0000 0000 0000 0000 0000 0001*2^24 = 167772157。
由於小數長度有23位這個限制,上述的兩個數,紅色部份都不會保存,即167772156和167772157在內存中保存是一樣的: 167772156 = 167772157。
驗證:
int main(int argc, char *argv[]) { float j,k; j = 16777216; k = 16777217; cout << j << endl; cout << k << endl; printf("%f\n",j); printf("%f\n",k); cout << (j=k?1:0) << endl; return 0; }
輸出:
1.67772e+007 1.67772e+007 16777216.000000 16777216.000000 1
如果不考慮精度的問題,float能保存的最大值是1.1111 1111 1111 1111 1111 111(1 1111 1111 …… 1111 共128-23=105個1)*2^128 = 3.4028236692093846346337460743177e+38。
由於紅色部份不會保存,因此這105位可以是1和0的任意組合,但他們最終都保存為1.1111 1111 1111 1111 1111 111*2^128,僅當這105位都為0,才沒有丟失精度,即有2^105-1個數都丟失精度。
float如此,double也一樣,只不過double長度為8個字節,可以存儲更精確的數據,但數據精度仍然是數據存儲時,不能回避的問題,所以在開發過程中,一定要選好正確的數據類型,以滿足業務的需要。