源:http://www.cnblogs.com/luguo3000/p/3719651.html
int與float是我們每天編程都用的兩種類型,但是我們真的足夠了解它們嗎。昨天在博客園看到一個比較老的笑話: “昨天晚上下班回家,一民警迎面巡邏而來。突然對我大喊:站住!民警:int 類型占幾個字節? 我:4 個。 民警:你可以走了。 我:為什么問這樣的問題? 民警:深夜還在街上走,寒酸苦逼的樣子,不是小偷就是程序員。”(注:看到有朋友評論說占幾個字節跟具體的環境有關,學過C++的都知道,在C++這樣的語言中確實取決於環境,但是在Java跟C#中不管什么環境都規定是4個字節,所以后邊我們只討論4個字節的情況)
看完這個笑話,我腦袋立馬將float、double等類型的字節長度閃了個遍。我知道float也占4個字節,但存儲結構跟int是不一樣的,並且表示范圍也不一樣。緊接着就出現了一個疑問,到底哪些int值是float不能表示的呢?如果你回答不了這個問題,那還是好好地了解一下吧,如果我說的不夠清楚,請多查點其他的資料看一下。
為什么有些int是float表示不了的呢?因為int與float同樣占4個字節,float表示的范圍又比int大並且還包含很多小數,那int的每個值都能被float表示就是不可能的事情了。在平時的編程中好像也沒有感覺什么不對呀,這是為什么呢?先把這個問題留到后邊,原理說清楚了再來回答這個問題。在文章的下邊帖了一個進制轉換程序,方便大家使用。
一. 小數十進制與二進制的轉換
二進制轉換成十進制:跟整數轉換一個原理,例如二進制11.11轉換為十進制 1*21+1*20+1*2-1+1*2-2=3.75。
十進制轉換成二進制:整數部分不用說了,跟整數的十進制轉成二進制沒有區別。小數部分采用乘2取整的方式,比如3.75整數部分對應的二進制是11。小數部分0.75,先乘以2等於1.5,取1.5的整數部分1。再用0.5(上次乘2的結果的小數部分)乘以2等於1.0,取1.0的整數部分1,現在已經沒有小數部分了,終止。0.75對應的二進制就是.11。
所以3.75對應的二進制是11.11。注意這里的3.75和1.11只是浮點數十進制與二進制的不同表示形式,存儲結構是一樣的,因為本來就是同一個數。內存結構又是怎么樣的呢,下邊介紹。
二. float的存儲結構
float也是占32位,第一位是符號位(sign),符號位后邊8位是指數(exponent),最后23位是尾數(mantissa)。
float值的二進制表示形式是:sign* mantissa* 2exponent。注意這個表達式是對應上述存儲結構的二進制。
符號位,表述浮點數的正或者負,0代表正,1代表負。
指數位,實際也是有正負的,但是沒有單獨的符號位,在計算機的世界里,進位都是二進制的,指數表示的也是2的N次冪,8位指數表達的范圍是0到255,而對應的實際的指數是-127到128。也就是說實際的指數等於指數位表示的數值減127。這里特殊說明,-127和+128這兩個指數數值在IEEE當中是保留的用作多種用途的,這里就不多做介紹了,有興趣的可以查閱其他資料。
尾數位,只代表了二進制的小數點后的部分,小數點前的那位被省略了,當指數位全部為0時省略的是0否則省略的是1,為什么呢,看個例子:
二進制11.11表示成指數形式是1.111*21,0.1111表示成指數形式是1.111*2-1。由此可見,正常情況下二進制的指數形式是肯定有一個1的,所以存儲的時候直接省略。但是在指數位全部為0時,指數是-127,這個數字是有特殊含義的,在尾數全部為0時代表的數值是0,省略的那位是0,如果省略的是1那么0這個數字就沒法用float表示了。
結合例子理解一下
那我們就看一下3.75的內存結構到底是什么樣子的。首先轉化成二進制形式11.11。轉化成二進制指數形式1.111*21。由此我們可以得知尾數部分是111(將1省略掉了),不足23位的后邊補0,指數部分是1+127=128,對應二進制10000000。所以存儲結構就是01000000011100000000000000000000。
反過來轉換一下,比如某個float的存儲結構是 01000000011100000000000000000000,符號位是正的,指數位是128,實際的指數是128-127=1,尾數是111,再加上省略的那位就是1.111。所以對應的二進制指數形式是1.111*21,對應的二進制是11.11,對應的十進制是3.75。
到這里我們就可以看出,實際上尾數決定了浮點數的精度,尾數只有23位,加上省略的那位就是24位。如果一個int類型的值小於224,那么float是完全可以表示的。如果int類型大於224就不一定能表示了。假如一個int數值的二進制表示形式是100000000000000000000000,表示成指數形式是1.00000000000000000000000*223,對應的float的類型,尾數位全部為0,指數位是23+127=150,這樣完全沒有問題。假如一個int數值的二進制表示形式是1000000000000000000000001,表示成指數形式是1.000000000000000000000001*224,對應的float的類型尾數位是000000000000000000000001一共24位,這樣就完全超出了float最多容納23位尾數的能力。所以就不能正確表達這個int值了。由此也可以得出不能被float准確表達的最小int值是224+1。我們再將1000000000000000000000001的值加1,變成了1000000000000000000000010,這樣變換為指數形式可以看出尾數又變為了23位,也就是說25位的二進制整數最后一位是0才能被float准確表示,每2個數就有一個不能被准確表示。如果是26位的二進制整數最后兩位都是0才可以被float准確表達,每4個數就有3個不能被准確表示,以此類推。
現在再來回答為什么在編程的過程中似乎沒怎么引起注意,這是因為,我們平時用的數值基本都小於224+1=16777217。
下邊是在網上找的一個二進制在線轉換器,方便大家使用。