1. 程式人生 > >C語言的int, float,double相互轉化(從本質上理解可能的問題)

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的相互轉化,請看如下程式碼:
/* 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.112
2 7/8 10.1112
1 7/16 1.01112

大家觀察一下,二進位制小數有什麼特點。
只能準確的表示x/2k的小數,而不為x/2k只能近似,請看下面的小數

十進位制小數 二進位制(Representation)
1/3 0.01010101[01]…2
1/5 0.001100110011[0011]…2
1/10 0.0001100110011[0011]…2

[0011]表示無限迴圈小數
為什麼會出現這樣的計算結果,請看下面1/3 和 1/5是如何計算的。

22+24+26.....+22n=n=122n=limn+14(122n)1(22)=13

1/5就複雜了點

23+24+27+28+211+212+.......+214n+24n=n=1214n+n=124n=limn+18(1214n)1(24)+limn+116(124n)1(24)=215+115=15

可見,當小數不能表示為

x2k的時候,二進位制小數只能使用近似,對於近似的方法(最在浮點數中最常用的close to even(靠近偶數))的方法,在下面介紹浮點數round的時候會說到。
那麼定點數有什麼缺點呢?很重要的一個缺點是定點數無法標準化,你無法給出一個標準的定點數計算方式,小數點該放哪裡,不同小數點的位置又給計算定點數增加了難度。同時,定點數表示的範圍太小了,一個32位的定點數,假設沒有整數位,全是小數位。那麼所能表示的小數的最小值為:
232,而32位浮點數光指數位最小都可以表示到2126,定點數從標準化和範圍大小都比較差,但好處就是定點數所能表示的數字精確度高。
- 這個時候,專家組為了統一二進位制小數,浮點數來表示二進位制小數的方式就出現了,那麼浮點數是什麼呢?下面用一個最簡單的式子表達出來:
V=(1)s×M×2E
s:表示符號位,只用一個bit表示
M:表示尾數(significand)(frac)也表示小數位,即能準確表示小數位
E:表示指數位,簡單來說就是位數的多大。
那麼,我們來看一下,我們最常用的float,double是怎麼組成的:
這裡寫圖片描述

明顯的看出,float有8位指數位,23位尾數位。指數最大可表示的範圍為-127~126,但浮點數的指數計算有一點技巧要用到:E-Bias。
下面是浮點數所表示的一個範圍:
這裡寫圖片描述
大家可以清楚的看到浮點數隨著大小的不同被分成好幾種,接近0的被稱為Denormalized,比較大的數字被分為Infinity,接下來介紹這幾種數字的特徵:
Normalized:這是最常見的一種情況,指數位EXP不為0(不小),EXP不全為1(不大)。此時,階碼(這個2E)E=e-Bias,e即指數位上計算得到的值,Bias=2k1-1,k表示指數位的位數,float單精度即32位浮點為127,double雙精度為1023。故float單精度的E範圍為-126~127,對於雙精度為-1022~+1023。
而對於尾數位,即小數位:相當於得到的數為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