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;
    }
}
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

這段程式碼計算一個數組所有元素之和,看起來似乎沒什麼問題。但是當你的陣列為空的時候,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.001111122,
      這裡寫圖片描述
      但是這樣的二進位制會出現什麼樣的問題呢?請看下面的二進位制小數
整數部分 小數部分 二進位制(Representation)
5 3/4 101.1122

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

十進位制小數 二進位制(Representation)
1/3 0.01010101[01]…22

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

22+24+26.....+22n=n=122n=limn+14(122n)1(22)=132−2+2−4+2−6.....+2−2n=∑n=1∞2−2n=limn→+∞14(1−2−2n)1−(2−2)=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=152−3+2−4+2−7+2−8+2−11+2−12+.......+21−4n+2−4n=∑n=1∞21−4n+∑n=1∞2−4n=limn→+∞18(1−21−4n)1−(2−4)+limn→+∞116(1−2−4n)1−(2−4)=215+115=15

可見,當小數不能表示為

x2k x2k 
s:表示符號位,只用一個bit表示
M:表示尾數(significand)(frac)也表示小數位,即能準確表示小數位
E:表示指數位,簡單來說就是位數的多大。
那麼,我們來看一下,我們最常用的float,double是怎麼組成的:
這裡寫圖片描述

明顯的看出,float有8位指數位,23位尾數位。指數最大可表示的範圍為-127~126,但浮點數的指數計算有一點技巧要用到:E-Bias。
下面是浮點數所表示的一個範圍:
這裡寫圖片描述
大家可以清楚的看到浮點數隨著大小的不同被分成好幾種,接近0的被稱為Denormalized,比較大的數字被分為Infinity,接下來介紹這幾種數字的特徵:
Normalized:這是最常見的一種情況,指數位EXP不為0(不小),EXP不全為1(不大)。此時,階碼(這個2EE-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
前一半為A,後一半為B

0110000 1 0111000 1
1011110 712712

可以看出第二,三,四的問題的關鍵在於進位
三的A為:0.11001,即到B先轉化為1.1001,明顯B的frac只有三位,所有根據close to even,需要接近偶數,所以,round down:1.100,符合。
比較有問題的是最後一個:000 0001 。首先,000說明是Denormalize,則該數表示為0.0001*22−2,由於B是4位exp,所以不會是最小的數,所以由Denormalize->Normalize,答案也為1/64
為什麼要選擇,close to even呢?如果全部的數字都為0.01要精確到小數點後一位的話,如果是四捨五入,那麼最後的誤差將是0.01*n,但如果是close to even的,認為偶數和奇數是等概率出現,就很小的避免誤差往一邊倒的情況。

  • 浮點數的計算
    首先先來兩個公式
    x+ff
    但是3.14+(1e10-1e10)=3.14
    le20*(le20-le20)=0.0
    le20*le20-le20*le20=NaN,由於溢位的關係,可見在數字大的情況下不滿足加法結合律和乘法分配律

最後,來看看double,float和int相互轉化可能的問題
這裡寫圖片描述
判斷以下式子是否正確:
這裡寫圖片描述
A.正確,因為double的frac為32位和int相同,不會丟失資訊。
B.錯誤,因為float的frac為23位小於int,會丟失資訊。
C.錯誤。double比float精度高。從double轉float會丟失資訊。
D.正確。
E.正確。符號數正負轉化只取決於符號位。
F.正確。浮點數在進行運算的時候會全部轉化為浮點數。
G.正確。
這裡寫圖片描述
H.錯誤。如果f+d溢位,結果為0.

從學了C語言之後,一直習慣於C/C++任意的強制轉化,但是C語言的強制轉化卻總是帶來意想不到的後果,在這裡,我將從int,float,double的本質上講解這些可能出現的問題以及解決辦法,在下面你將看到:
這裡寫圖片描述
OK,現在好戲開始。