int與float深入理解

別在int與float上栽跟頭


        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*2^1+1*2^0+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* 2^exponent。注意這個表達式是對應上述存儲結構的二進制。

        符號位,表述浮點數的正或者負,0代表正,1代表負。


        指數位,實際也是有正負的,但是沒有單獨的符號位,在計算機的世界裏,進位都是二進制的,指數表示的也是2的N次冪,8位指數表達的範圍是0到255,而對應的實際的指數是-127到128。也就是說實際的指數等於指數位表示的數值減127。這裏特殊說明,-127和+128這兩個指數數值在IEEE當中是保留的用作多種用途的,這裏就不多做介紹了,有興趣的可以查閱其他資料。

        尾數位,只代表了二進制的小數點後的部分,小數點前的那位被省略了,當指數位全部爲0時省略的是0否則省略的是1,爲什麼呢,看個例子:

        二進制11.11表示成指數形式是1.111*2^1,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*2^1,對應的二進制是11.11,對應的十進制是3.75。


        到這裏我們就可以看出,實際上尾數決定了浮點數的精度,尾數只有23位,加上省略的那位就是24位。如果一個int類型的值小於224,那麼float是完全可以表示的。如果int類型大於224就不一定能表示了。假如一個int數值的二進制表示形式是100000000000000000000000,表示成指數形式是1.00000000000000000000000*2^23,對應的float的類型,尾數位全部爲0,指數位是23+127=150,這樣完全沒有問題。假如一個int數值的二進制表示形式是1000000000000000000000001,表示成指數形式是1.000000000000000000000001*2^24,對應的float的類型尾數位是000000000000000000000001一共24位,這樣就完全超出了float最多容納23位尾數的能力。所以就不能正確表達這個int值了。由此也可以得出不能被float準確表達的最小int值是2^24+1。我們再將1000000000000000000000001的值加1,變成了1000000000000000000000010,這樣變換爲指數形式可以看出尾數又變爲了23位,也就是說25位的二進制整數最後一位是0才能被float準確表示,每2個數就有一個不能被準確表示。如果是26位的二進制整數最後兩位都是0纔可以被float準確表達,每4個數就有3個不能被準確表示,以此類推。


        現在再來回答爲什麼在編程的過程中似乎沒怎麼引起注意,這是因爲,我們平時用的數值基本都小於224+1=16777217。


原文鏈接:別在int與float上栽跟頭



java中的float和double的精度問題


1、背景知識 
在java中沒有細講,只是講了float佔32位(bit),double佔 64位。 
對於計算機來說,用位數表示是合適的。但有人喜歡用字節(byte)表示。一個字節佔8位。 
1 byte = 8 bit. 
所以float佔4個字節,double佔8個字節。 
不過我還是喜歡用位數表示。這樣更直接,也更容易理解計算機是怎麼存儲這些類型的。 

對於精度和範圍,還是參考一下c++吧。 



2、存儲知識 
計算器存儲浮點數的方法:(用科學計數法存儲) 
將要存的數先轉換爲小數(0.xxxxxx)x10的n次冪的形式進行存儲。 
例如: 
3.1415 將被轉換爲:0.31415 x 10^1 
100000 將被轉換爲:0.1 x 10^6 

首先申明一點,先範圍(有效數字位,包括整數位和小數位),再精度。 



3、下面切入正題 
===================== 
在c++中單精度float類型與雙精度double類型的問題 

【"單精度用float表示,在計算機中使用4位字節(32位bit)來表示,具有7位有效數字"】 

float類型存儲的時候1個bit是符號位,8個bit是指數位,剩下的23個bit是有效數字位。 
2的23次方是8388608,即7位有效數字,精度(10進制)。 

一個單精度的浮點數在內存當中佔用了32個bit位,按照浮點數的表示標準,最高位表示符號,這32位一部分用來表示階碼,一部分用來表示小數部分。 
按照這個表示方法轉化成10進制之後,它能表示的最高精度是7位有效數字。 

比如 
float a=3.14159;a在內存中實際上表示爲0.314159乘以10的1次方(0是符號位),而分配給a的存儲單元就分爲兩部分,一部分存0.314159,一部分存指數1,而且也都是轉化爲2進制來存。 

================== 
float ,1位符號位, 8位指數位,23位尾數位 
double,1位符號位,11位指數位,52位尾數位 

float尾數位23位,2^23=8.3E6,7位,所以不同的編譯器規定不同,有些是7位,有些8位 
double尾數52位,2^52=4.5E15,15位,所以double的有效位數是15位 


後記: 
數一下有效數字位數(整數位+小數位),7位以內的用float,15位以內的用double 
但是還有一點小小的區別: 
float f =  (float) 62345678.912345;  // => 6.234568E7  共 7 位 
float f2 =  (float) 12345678.912345; // => 1.2345679E7 共 8 位 

(精度問題,float精度爲7--8位,8位的情況是第一位是1,當是2時進位後面的精度丟失?)

原文鏈接:java中的float和double的精度問題



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章