1. 程式人生 > >有符號數、無符號數理解

有符號數、無符號數理解

大家都知道,在C/C++中,對於w位編譯器,其有符號數表示的數值範圍為-2 ^ (w-1)~2 ^(w-1)-1,無符號數表示的數值範圍為0 ~ 2 ^ w-1,舉個例子,在16位編譯器中,有符號數的數值範圍為-2 ^ 31 ~ 2 ^ 31-1,無符號數的數值範圍為0 ~ 2 ^ 32-1。那麼,有符號數和無符號數的區別在哪?同樣都是以32位2進位制位來表示(後文均以32位為準),為什麼各自表示的數值範圍不同呢,又為什麼是這個範圍呢?

1.有符號數與無符號數的區別

為了解釋上面的問題,就不得不再提到另一個名詞——“補碼”,什麼是補碼呢?在《深入理解計算機系統》一書中,有這樣一段話:“對於許多應用,我們還希望表示負數值。最常見的有符號數的計算機表示方式就是補碼形式。在這個定義中,將字的最高有效位解釋為負權”

,這句話的意思其實很簡單,就是32位二進位制位中,最高位的權重為-1,換句話說,在無符號數中,最高位所代表的值是2 ^ 31,而在有符號數中,其表現方式是補碼形式,最高位所代表的值是- 2^ 31。為了更加形象的來理解這句話的意思,我們用-1來作為一個例子。
在這裡插入圖片描述
有一點值得注意的是:通常情況下,數字都預設為有符號型別。 因此這裡的int a實際上就是signed int a;可見-1的二進位制位表示為“11111111 11111111 11111111 11111111”。從這32中的最低位開始,每一位分別表示2 ^ 0、2 ^ 1、2 ^ 2、2 ^ 3…2 ^ 30,但是最高位就不一樣了,按照有符號數的補碼形式特點,其最高位的1表示的是負權的-2 ^ 31,這樣,我們把每一位所表示的值加起來就是 - 2 ^ 31 +2 ^ 30+…+2 ^ 1+2 ^ 0=- 2 ^ 31+2 ^ 31-1=-1,可見其剛好就等於-1了,如果我們把程式中的a換成unsigned int型別,那麼按前面所說的,最高位的1就應當表示為2 ^ 31,那麼unsigned int(a)的值就應當是2 ^ 31 +2 ^ 30+…+2 ^ 1+2 ^ 0=2^32-1=4294967295,我們來看看是不是這樣的呢:
在這裡插入圖片描述

可以看到,剛好跟我們想的是一樣的,反過來,如果先定義一個unsigned 型別的,假設其為2147483649,很明顯,這個數已經超出了有符號數的上限,它的二進位制表示為“10000000 00000000 00000000 00000001”,按照前面分析的,這裡的最高位1表示的是2 ^ 31,把每一位加起來就是2 ^ 31+2^0=2147483649,如果將其轉換為signed型別的話,那麼最高位1表示的就是-2 ^ 31,那麼轉換後的(signed)2147483649就應當為-2 ^ 31+2 ^0=-2147483647,我們來驗證一下:
在這裡插入圖片描述
可以看到,執行結果完全符合我們的猜想。這就說明了有符號數和無符號數的區別:在32位編譯器中,有符號數的二進位制位最高位表示-2^ 31,而無符號數的二進位制位最高位表示的是2^31。根據這一區別,我們也不難得到有符號數與無符號數的一些轉換原理。

2.有符號數與無符號數的轉換

2.1 轉換公式

前面已經知道了有符號數與無符號數的區別,那麼實際上就很容易得出二者的轉換關係:假設一個數x,無論它是有符號數還是無符號數,它的二進位制表示肯定都是唯一的(不可能在有符號形式下有一種表示,在無符號形式下也有一種表示),那麼假設其二進位制位中的最高位為m(m=0或1),其餘位組合表示的數為n,打個比方,10的二進位制表示為1010,那麼它的最高位就是m=1,n=2(010),那麼很明顯,x=m*2^(w-1)+n,其中w為這個數的二進位制位數,在32位編譯器中w=32,64位編譯器中w=64。

以32位編譯器為例,對於無符號數,由於其最高位代表2^31,因此x=m * 2 ^ 31+n ;而對於有符號數而言,由於其最高位代表-2 ^31,因此x=-m * 2 ^31+n,因此,無符號數要想轉換為有符號數,就需要減上m * 2 ^32,那什麼時候m為0什麼時候m為1呢?很簡單,對於無符號數來說,m為1表示x>=2 ^31,否則m=0;對於有符號數來說,m=1表示x<0否則m=0。由此得到轉換公式如下:轉換公式如下:
在這裡插入圖片描述

其中U代表無符號數,S代表有符號數,w表示編譯器的位數。

2.2 顯示轉換

顯示轉換的方式如下所示:
在這裡插入圖片描述
程式不用多說,顯示轉換還是很簡單的,顯示轉換就是一種強制型別轉換。

2.3 隱式轉換

隱式轉換主要在以下兩種情況下發生:
①當一種型別的表示式被賦值給另外一種型別的變數時;
②當執行一個運算時,如果它的一個運算數是有符號的而另一個是無符號的,那麼就會隱式地將有符號引數強制型別轉換為無符號數。

對於第①種情況,如下所示:

可見,輸出的並不是-1,為什麼不是-1而是4294967295呢?原因就在於unsigned int a=-1;這一句,前面說過
通常情況下,數字都預設為有符號型別。 因此這裡的-1就是有符號型別,當它被賦值無符號型變數a時,-1就被隱式轉換為無符號型別了,因此這裡就需要採用前面的轉換公式,-1+2^32=4294967295。

對於第②種情況,如下所示:
在這裡插入圖片描述
如圖所示,a=1,本身是大於-1的,應該返回true,但是由於這裡a為無符號數,-1是有符號數,在進行“>”運算時,-1被強制轉換成了無符號數,即成了4294967295,因此返回的真值是1>4294967295的真值結果,就是false。
這種情況往往對於標準的加減乘除運算來說並沒有多大差異,但是對於“>”、“<”這樣的關係運算符來說,結果就是非直觀的了。