int float double 的那點小秘密

2021-06-22 08:10:26 字數 2553 閱讀 9310

偶編c的時候,有的時候就是因為這些float int double之間的混算錯得一塌糊塗。。。這位仁兄說得挺好的,不過要想在深入了解下去,可就麻煩了。如果沒那知識,就一定要注意強制型別轉換後賦值和運算,比較保險!

原文如下:

抱歉我用了乙個這麼「二」的題目,不過二點就二點吧,希望內容還不算太二。

其實學習過程式設計的同學,都對這三個東西再熟悉不過了。int,又稱作整型,在.net中特指的是int32,為32位長度的有符號整型變數。float,單精度浮點數,32位長度,1位符號位,8位指數字與23位資料位,在.net中又稱為single。double,64位長度的雙精度浮點數,1位符號位,11位指數字,52位資料位。它們互相的關係就是:int可以穩式轉換成float和double,float只能強制轉換成int,但是可以隱式轉換成double,double只能強制轉換成float和int。

在說明問題之前,還很有必要溫習一下計算機組成原理時學習到的一些知識,就是二進位制補碼表示以及浮點數表示。我想把乙個十進位制轉化為二進位制的方法已經不用多費唇舌,只不過為了計算方便以及消除正零與負零的問題,現代計算機技術,記憶體裡存的都是二進位制的補碼形式,當然這個也沒什麼特別的,只不過有某些離散和點,需要特殊定義而已,比如-(2^31),這個數在int的補碼裡表示成1000…(31個零),這個生套補碼計算公式並不能得到結果(其實不考慮進製的話還真是這個結果,但是總讓人感覺很怪)。再者,浮點數,其實就是把任何二進位制數化成以0.1....開頭的科學計數法表示而已。

廢話說完,這就出現了幾個問題,而且是比較有意思的問題。

1 int i = int32.maxvalue;

2 float f = i;

3 int j = (int)f;

4 bool b = i == j;

這裡的b,是false。剛才這個操作,如果我們把float換成long,第一次進行隱式轉換,第二次進行強制轉換,結果將會是true。乍一看,float.maxvalue是比int.maxvalue大了不知道多少倍的,然而這個隱式轉換中,卻造成了資料丟失。int.maxvalue,這個值等於2^31-1,寫成二進位制補碼形式就是01111…(31個1),這個數,在表示成float計數的科學計數法的時候,將會寫成+0.1111…(23個1)*2^31,對於那31個1,裡面的最後8個,被float無情的拋棄了,因此,再將這個float強制轉換回int的時候,對應的int的二進位制補碼表示已經變成了0111…(23個1)00000000,這個數與最初的那個int相差了255,所以造成了不相等。

那麼提出另乙個問題,什麼樣的int變成float再變回來,和從前的值相等呢?這個問題其實完全出在那23位float的資料位上了。對於乙個int,把它寫成二進位制形式之後,成為了個一32個長度的0、1的排列,對於這個排列,只要第乙個1與最後乙個1之前的間距,不超過23,那麼它轉換成float再轉換回來,兩個值就會相等。這個問題是與大小無關的,而且這個集合在int這個全集下並不連續。

1 double d = 0.6;

2 float f = (float)d;

3 double d2 = f;

4 bool b = d == d2;

這裡的b,也是false。剛才這個操作,如果開始另d等於0.5,結果就將會是true。乍一看,0.6這個數這麼短,double和float都肯定能夠表示,那麼轉換過去再轉換回來,結果理應相等。其實這是因為我們用十進位制思考問題太久了,如果我們0.6化成二進位制小數,可以發現得到的結果是0.10011001……(1001迴圈)。這是乙個無限迴圈小數。因此,不管float還是double,它在儲存0.6的時候,都無法完全儲存它精確的值(計算機不懂分數,呵呵),這樣的話由於float儲存23位,而double儲存52位,就造成了double轉化成float的時候,丟失掉了一定的資料,非再轉換回去的時候,那些丟掉的值被補成了0,因此這個後來的double和從前的double值已經不再一樣了。

這樣就又產生了乙個問題,什麼樣的double轉換成float再轉換回來,兩個的值相等呢?其實這個問題與剛才int的那個問題驚人的相似(廢話,都和float打交道,能不相似麼),只不過我們還需要考慮double比float多了3位的指數字,太大的數double能表示但float不行。

還有乙個算是數學上的問題,什麼樣的十進位制小數,表示成二進位制不是無限小數呢?這個問題可以說完全成為數學範疇內的問題了,但是比較簡單,答案也很明顯,對於所有的最後一位以5結尾的十進位制有限小數,都可以化成二進位制的有限小數(雖然這個小數可能長到沒譜)。

最後,乙個有意思有問題,剛才說過0.6表示成為二進位制小數之後,是0.1001並且以1001為迴圈節的無限迴圈小數,那麼在我們將它存成浮點數的時候,一定會在某個位置將它截斷(比如float的23位和double的52位),那麼真正存在記憶體裡的這個二進位制數,轉化回十進位制,到底是比原先的十進位制數大呢,還是小呢?答案是it depends。人計算十進位制的時候,是四捨五入,計算機再計算二進位制小數也挺簡單,就是0舍1入。對於float,要截斷成為23位,假如卡在24位上的是1,那麼就會造成進製,這樣的話,存起來的值就比真正的十進位制值大了,如果是0,就捨去,那麼存起來的值就比真正的十進位制值小了。因此,這可以合理的解釋乙個問題,就是0.6d轉換成float再轉換回double,它的值是0.60000002384185791,這個值是比0.6大的,原因就是0.6的二進位制科學計數法表示,第24位是1,造成了進製。

int,float,double型轉換深析

int整型 net中特指int32為32位長度符號整型變數 float 單精度浮點數32位長度1位符號位8位指數字與23位資料位 net中又稱為single double 64位長度雙精度浮點數1位符號位11位指數字52位資料位 它們互相關係就 int可以穩式轉換成float和double,floa...

int float double 最大值,最小值

中沒有double的最大最小值。如果輸出的比如 100lf輸出2.23432432,沒有達到100位,則最後2後面不一定都是0。原文見view plaincopy to clipboardprint?coder acboy date 2010 3 1 include include using na...

container of 的的的原理

另外一篇,同樣精彩,揭開linux核心中container of的神秘面紗 華清遠見嵌入式學院講師。在linux 核心中有乙個大名鼎鼎的巨集container of 這個巨集是用來幹嘛的呢?我們先來看看它在核心中是怎樣定義的。呵呵,乍一看不知道是什麼東東。我們先來分析一下container of p...