計算機是怎么存小數的?
1000.101
這種二進制小數是「定點數」形式,代表着小數點是定死的,不能移動,如果你移動了它的小數點,這個數就變了, 就不再是它原來的值了。然而,計算機並不是這樣存儲的小數的,計算機存儲小數的采用的是浮點數,名字里的「浮點」表示小數點是可以浮動的,比如
1000.101
這個二進制數,可以表示成1.000101 x 2^(-3)
,類似於數學上的科學記數法。既然提到了科學計數法,我再幫大家復習一下,比如有個很大的十進制數 1230000,我們可以也可以表示成
1.23 x 10^6
,這種方式就稱為科學記數法,該方法在小數點左邊只有一個數字,而且把這種整數部分沒有前導 0 的數字稱為規格化,比如1.0 x 10^(-9)
是規格化的科學記數法,而0.1 x 10^(-9)
和10.0 x 10^(-9)
就不是了。因此,如果二進制要用到科學記數法,同時要規范化,那么不僅要保證基數為 2,還要保證小數點左側只有 1 位,而且必須為 1,所以通常將
1000.101
這種二進制數,表示成1.000101 x 2^(-3)
,其中,最為關鍵的是 000101 和 -3 這兩個東西,它就可以包含了這個二進制小數的所有信息,000101
稱為尾數,即小數點后面的數字,-3
稱為指數,指定了小數點在數據中的位置。現在絕大多數計算機使用的浮點數,一般采用的是 IEEE 制定的國際標准,這種標准形式如下圖:
這三個重要部分的意義如下:
符號位:表示數字是正數還是負數,為 0 表示正數,為 1 表示負數;
指數位:指定了小數點在數據中的位置,指數可以是負數,也可以是正數,指數位的長度越長則數值的表達范圍就越大;
尾數位:小數點右側的數字,也就是小數部分,比如二進制 1.0011 x 2^(-2),尾數部分就是 0011,而且尾數的長度決定了這個數的精度,因此如果要表示精度更高的小數,則就要提高尾數位的長度;
用
32
位來表示的浮點數,則稱為單精度浮點數,也就是我們編程語言中的float
變量,而用64
位來表示的浮點數,稱為雙精度浮點數,也就是double
變量,它們的結構如下:
以上引用部分主要來自參考資料1,該參考資料中還有很多有用的相關知識,可以移步學習。
float 的尾數部分是 23 位,同時都帶有一個固定隱含位(規定必須為1),故float一共24個二進制有效位。將24個二進制有效位都看作無符號正整數A,則當8 388 608=2^23≤A<10 000 000, A需要7位十進制才可表示,而不確定位最壞的情況下會影響到第7位,所以至少能保證6位有效數字是確定的;當10 000 000≤A≤ 2^24-1=16 777 215時,A需要8位十進制才可表示,而不確定位最壞的情況下會影響到第8位,所以至少能保證7位有效數字是確定的。
double 的尾數部分是 52 位,同時都帶有一個固定隱含位(規定必須為1),故float一共53個二進制有效位。將53個二進制有效位都看作無符號正整數A,因為某16位數=2^52≤A≤ 2^53-1=某16位數, 故A需要16位十進制才可表示,而不確定位最壞的情況下會影響到第16位,所以至少能保證15位有效數字是確定的。
因此,float最低能保證6個有效位,double最低能保證15個有效位。當然最低能保證,不代表超出的位數就不准確:(1)float中,當10 000 000≤A≤ 2^24-1=16 777 215時,則相應的float最低能保證7個有效位。(2)不確定位不一定確實對末位(尾數區的最右邊一位)產生了不確定性影響;甚至末位更后的位的值也可能是准確的。
參考資料:
1. https://blog.csdn.net/flynetcn/article/details/115741534?spm=1001.2014.3001.5506
2. ieee 定義中如何解釋 float 精度為6到7位? - BlueWanderer的回答 - 知乎 https://www.zhihu.com/question/479335091/answer/2065860323