補碼和反碼


Q1:int類型在內存中是以何種方式存儲的?

要解決這個問題,我們需要首先比較深入地理解下int類型。

{

本文中的int類型的相關數據,如無特別說明都以32位操作系統下的VC++6.0編譯器環境為准。

在下表中可以看到,int類型表示帶有符號的整型,而unsigned int類型為無符號的整型。

類型名稱

占字節數

取值范圍

int

4B

-2^31~2^31-1

unsigned int

4B

0 ~ 2^32

 

1、占用的比特位數量

在32位操作系統下,兩者都是占用4個字節,每個字節有8個比特位,因此有32個0-1的二進制位數。兩者的不同在於,int類型有正負號(±)的存在,需要比unsigned int類型多消耗一個位數。

2、符號的表示方法

    在所有被int類型占用的比特位中,左起第一個位(即最高位)就是符號位。int類型的符號位上,0表示正數,1表示負數。在32位操作系統下,其余后面31位是數值位。

3、數字0的表示方法

    按照上面提到的符號,我們有了兩種0的表示方法,即“+0”和“-0”。

    實際上,在32位系統下int類型中,我們計算機已經強行規定了這種情況,數字0采用“+0”的表示方法,即0000000000000000 00000000;而“-0”這個特殊的數字被定義為了-2^31。

    因此我們看到32位系統下int類型的取值范圍中,負數部分比正數部分多了一個數字,正數的最大取值是2^31-1,而負數的最小取值是-2^31。正數部分之所以要減去1,是因為被數字0占用了“+0”,而負數部分不需要用來表示0,因此原本的“-0”就用來表示-2^31這個數字。

}

那么是不是實現了上面已經提到的int類型的深入理解,我們就可以知道內存中int類型的數據表達了呢?

比如int類型的數字“-1”,按照上面的理解方式,在內存中32個比特位上應該是這樣子的:10000000 00000000 00000001,左邊第一個1表示負號,后面31位表示數值部分“1”。實際情況並不是這樣。這里就需要引入“補碼”這個概念了。

Q2:什么是“補碼”?

要回到這個問題,得額外補充兩個概念,“原碼”和“反碼”。

{

    計算機中的符號數有三種表示方法,即原碼、反碼和補碼。三種表示方法均有符號位和數值位兩部分,符號位都是用0表示“正”,用1表示“負”,而數值位,三種表示方法各不相同。

1、原碼(true form)

    原碼,是計算機中一種對數字的二進制定點表示方法。原碼表示法在數值前面前面有一位符號位(即最高位為符號位),正數該位為0,負數該位為1(0有兩種表示:+0和-0),其余位表示數值的大小。

    怎么樣,是不是覺得眼熟,沒錯!Q1中結尾提到的int類型數值“-1”的32位二進制就是原碼,即10000000 00000000 00000001。與之對應的,正數“+1”就是00000000 00000000 00000001。

    那么為何不用原碼在內存中表示數值呢?

我們舉個例子(以8位二進制表示)

十進制

原碼

1

0000 0001

-1

1000 0001

結果(原碼)

1000 0010

結果(十進制)

-2

上述結果換算成十進制為-2,這顯然出錯了。這是由於計算機在計算時以加法進行計算的算法更簡便,減法先轉換為負數,再進行加法運算。因此,原碼的符號位不能直接參與運算。

總結:原碼是有符號數的最簡單的編碼方式,便於輸入輸出,但作為代碼加減運算時較為復雜,故計算機一般不采用這種編碼方式存儲符號數。

2、反碼(ones' complement)

    首先我們來了解下反碼表示法的規定:“正數的反碼與其原碼相同;負數的反碼是對其原碼逐位取反,但符號位除外。”

    什么意思呢?舉個例子說明下:

①  對於正數和“+0”而言,其原碼本身就是反碼,例如 8位二進制“+1”,其原碼與反碼都是00000001;

②  對於負數和“-0”而言,符號位與原碼中一樣,保持不變,其余位數逐位取反,1換成0,0換成1,例如 “-1”,其8位二進制原碼是10000001,其反碼是1111 1110;

那么我們是否已經可以正常進行運算了呢?

我們舉個三個例子:

例一:1+2=3(以8位二進制表示)

十進制

原碼

反碼

1

0000 0001

0000 0001

2

0000 0010

0000 0010

結果(反碼)

 

0000 0011

結果(原碼)

 

0000 0011

結果(十進制)

 

3

計算結果正確。

例二:1+(-2)=-1

十進制

原碼

反碼

1

0000 0001

0000 0001

-2

1000 0010

1111 1101

結果(反碼)

 

1111 1110

結果(原碼)

 

1000 0001

結果(十進制)

 

-1

    計算結果正確。

例三:1+(-1)=0

十進制

原碼

反碼

1

0000 0001

0000 0001

-1

1000 0001

1111 1110

結果(反碼)

 

1111 1111

結果(原碼)

 

1000 0000

結果(十進制)

 

-0

計算結果為-0,問題來了,由於-0的存在,使得二進制與十進制的互換不再是一一對應的關系。

總結:由於-0這個問題的存在,會使得計算機需要增加額外的物理硬件配合運算,所以在計算機發展的早期就已經拋棄了使用反碼儲存數據。

3、補碼

    補碼正是基於反碼的“-0”問題誕生的,可以解決這個問題。

    補碼的計算方法是:正數和+0的補碼是其原碼,負數則先計算其反碼,然后反碼加上1,得到補碼。

    補碼換算為原碼的過程中,如果補碼是正數或者+0的補碼,則其原碼就是補碼本身;如果補碼是負數或者-0的補碼,則其原碼的計算方法是,先將補碼減掉1,得到反碼,再將反碼取反,得到原碼。

    以上的說法有些繞,但是補碼的算法應該已經說清楚了。下面舉一些例子。

例一:1+(-1)=0

十進制

原碼

反碼

補碼

1

0000 0001

0000 0001

0000 0001

-1

1000 0001

1111 1110

1111 1111

結果(補碼)

 

 

0000 0000

結果(反碼)

 

 

0000 0000

結果(原碼)

 

 

0000 0000

結果(十進制)

 

 

+0

計算結果正確,+0即是數字0的唯一表示。

例二:1+2=3

十進制

原碼

反碼

補碼

1

0000 0001

0000 0001

0000 0001

2

0000 0010

0000 0010

0000 0010

結果(補碼)

 

 

0000 0011

結果(反碼)

 

 

0000 0011

結果(原碼)

 

 

0000 0011

結果(十進制)

 

 

3

計算結果正確。

例三:1+(-2)=-1

十進制

原碼

反碼

補碼

1

0000 0001

0000 0001

0000 0001

-2

1000 0010

1111 1101

1111 1110

結果(補碼)

 

 

1111 1111

結果(反碼)

 

 

1111 1110

結果(原碼)

 

 

1000 0001

結果(十進制)

 

 

-1

計算結果正確。

特別地,我們加入例四:(-1)+(-127)=-128

我們知道8位二進制的符號數的取值范圍是(-2^7)~(2^7-1),即-128~127。

十進制

原碼

反碼

補碼

-1

1000 0001

1111 1110

1111 1111

-127

1111 1111

1000 0000

1000 0001

結果(補碼)

 

 

1000 0000

結果(反碼)

 

 

 

結果(原碼)

 

 

 

結果(十進制)

 

 

-128

由於補碼10000000具有特殊性,計算機在編寫底層算法時,將其規定為該取值范圍中的最小數-128,其值與(-1)+(-127)的計算結果正好符合。

補充一點,8位二進制補碼1000 0000沒有對應的反碼和原碼,其他位數的二進制補碼與此類似。

}

通過以上兩個問題Q1和Q2的回答和引申,我們已經知道int類型在內存中存儲的方式,即int類型在內存中,以補碼的形式存儲。而且我們還知道了為何int類型的取值范圍中負數的最小值的絕對值比正數的最大值大1的原因,即-2^31的補碼是1000000000000000 00000000,原本-0的位置被-2^31取代了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM