發布時間:2011-08-29 共1頁
抱歉我用了一個這么“二”的題目,不過二點就二點吧,希望內容還不算太二。
其實學習過編程的同學,都對這三個東西再熟悉不過了。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,造成了進位。
到了這里,仍然有一事不解,就是對于浮點數,硬件雖然給予了計算上的支持,但是它與十進制之間的互相轉換,到底是如何做到的呢,又是誰做的呢(匯編器還是編譯器)。這個東西突出體現在存在內存里的數明顯實際與0.6不等,但是無論哪種語言,都能夠在Debug以及輸入的時候,將它正確的顯示成 0.6提供給用戶(程序員),最好的例子就是double和ToString方法,如果我寫double d=0.59999999999999999999999999999,d.ToString()給我的是0.6。誠然,對于double來說,我寫的那個N長的數與0.6在內存里存的東西是一樣的,但是計算機,又如果實現了將一個實際與0.6不相等的數變回0.6并顯示給我的呢?關于這個問題,歡迎大家討論并請高手指教一二。