C語言的int, float,double相互轉化(從本質上理解可能的問題)
從學了C語言之後,一直習慣於C/C++任意的強制轉化,但是C語言的強制轉化卻總是帶來意想不到的後果,在這裡,我將從int,float,double的本質上講解這些可能出現的問題以及解決辦法,在下面你將看到:
OK,現在好戲開始。
- int
- unsigned int: unsigned int所進行的是模數計算,就是正常的二進位制相加減,計算方法和十進位制加減並無區別,但是unsigned int有著正溢位和負溢位的問題,如下圖計算所示:
這一點是我們需要注意的地方。 - int:int所使用的是32位補碼,關於補碼的運算,在這裡就不贅述了,大部分計算機導論的書籍都有相關說明。
- 接下來,要說的就是unsigned int和int的相互轉化,請看如下程式碼:
- unsigned int: unsigned int所進行的是模數計算,就是正常的二進位制相加減,計算方法和十進位制加減並無區別,但是unsigned int有著正溢位和負溢位的問題,如下圖計算所示:
/* WARNING: This is buggy code */
float sum_elements(float a[],unsigned length)
{
int i;
float result=0;
for(i=0;i<=length-1;i++)
{
result+=a[i];
return result;
}
}
這段程式碼計算一個數組所有元素之和,看起來似乎沒什麼問題。但是當你的陣列為空的時候,length輸入0之後,卻返回一個儲存器錯誤,這是為什麼呢?請看上文關於unsigned int計算的式子,length是unsigned int 型別,進行的是模數運算,只代表正數,如果出先了0000000(這裡有32個0)-00000..01(31個0,1個1)=111…11111(32個1)=UMAX。一個本該為-1的數變成了無符號數最大值,當然,當i取任何不為0的數都發生了非法訪問,自然出現了儲存器錯誤,並且任何數都小於UMAX,就會出現判別式永遠為真,出現死迴圈。解決這個問題的方法有兩種,做一個判斷,當傳入length<1,直接返回0.或者,在之前就將length轉化為int。
- 浮點數(float,double的理解)
- 什麼是定點數,定點數有什麼缺點:
我們用二進位制數表示整數,我們也想用二進位制表示小數。自然而然,我們會像十進位制的小數一樣,在二進位制上加上小數點,例如1.00111112 ,
但是這樣的二進位制會出現什麼樣的問題呢?請看下面的二進位制小數
- 什麼是定點數,定點數有什麼缺點:
整數部分 | 小數部分 | 二進位制(Representation) |
---|---|---|
5 | 3/4 | 101.11 |
2 | 7/8 | 10.111 |
1 | 7/16 | 1.0111 |
大家觀察一下,二進位制小數有什麼特點。
只能準確的表示
十進位制小數 | 二進位制(Representation) |
---|---|
1/3 | 0.01010101[01]… |
1/5 | 0.001100110011[0011]… |
1/10 | 0.0001100110011[0011]… |
[0011]表示無限迴圈小數
為什麼會出現這樣的計算結果,請看下面1/3 和 1/5是如何計算的。
1/5就複雜了點
可見,當小數不能表示為
那麼定點數有什麼缺點呢?很重要的一個缺點是定點數無法標準化,你無法給出一個標準的定點數計算方式,小數點該放哪裡,不同小數點的位置又給計算定點數增加了難度。同時,定點數表示的範圍太小了,一個32位的定點數,假設沒有整數位,全是小數位。那麼所能表示的小數的最小值為:
2
- 這個時候,專家組為了統一二進位制小數,浮點數來表示二進位制小數的方式就出現了,那麼浮點數是什麼呢?下面用一個最簡單的式子表達出來:
s:表示符號位,只用一個bit表示
M:表示尾數(significand)(frac)也表示小數位,即能準確表示小數位
E:表示指數位,簡單來說就是位數的多大。
那麼,我們來看一下,我們最常用的float,double是怎麼組成的:
明顯的看出,float有8位指數位,23位尾數位。指數最大可表示的範圍為-127~126,但浮點數的指數計算有一點技巧要用到:E-Bias。
下面是浮點數所表示的一個範圍:
大家可以清楚的看到浮點數隨著大小的不同被分成好幾種,接近0的被稱為Denormalized,比較大的數字被分為Infinity,接下來介紹這幾種數字的特徵:
Normalized:這是最常見的一種情況,指數位EXP不為0(不小),EXP不全為1(不大)。此時,階碼(這個2
而對於尾數位,即小數位:相當於得到的數為1.M(M表示尾數位)
下面就到了重點了,這也是浮點數經常被大家忽略的地方。
Denormalized:當階數E全為0的時候,被稱為Denormalized,那麼它的指數位就變成了E=1-Bias, 之所以不用-Bias,而用1-Bias,是為了實現與Normalized的數實現完美過渡,具體如何過渡的圖片會在下面給出。
而Denormalized的尾數有什麼特點呢:如果frac為0,說明該數為0,但是不知道是+0還是-0。因為,前面的符號位未知。如果frac不為0的話,那麼實際的數字表示為0.M(M為尾數位),記住,此時前面是0.,因為只有是0.最終才能接近0
Infinity:當指數位全為1,frac尾數位為0的時候表示Infinity(可以表示無窮大),分別取符號位為1或者0,表示正無窮或負無窮。可以滿足Infinity相乘或除,表示溢位。
NaN:not a number,即指數位全為1,frac尾數位不全為0.
一張圖可以表示Normailized,Denormalized,Infinity,NaN
這張圖說明,從Denormalized到NaN有什麼變化:
可以看到在Denormalize使用E=1-Bias,並且M前取0,實現了從Largest denorm到Smallest norm完美過渡。
- 浮點數的rounding
上文提到無論是定點數還是浮點數都只能表示有限的位數,那麼舍入就顯的是一個很重要的環節了。浮點數採取的舍入方法,小於一半的向下舍入,大於一半的向上舍入,在中間的,close to even(向偶數舍入),下面是幾個二進位制例子:
Format A:
There are k=3 exponent bits. The exponent bias is 3.
There are n=4 fraction bits.
Format B:
There are k=4 exponent bits. The exponent bias is 7.
There are n=3 fraction bits.
要求給出A,將A轉化為B