二進制數的編碼表示
例如,8位二進制數A = (+1011011),B = (-1011011),它們在機器中可以表示為:
A:01011011
B:11011011
計算機中幾種常用的編碼----原碼、反碼和補碼。
1.原碼
將符號位數字化為0或1,數的絕對值與符號一起編碼,即所謂"符號-絕對值表示"的編碼,稱為原碼。
例如:
整數:
X = +1011011,原碼:[X]原 = 01011011;
X = -1011011,原碼:[X]原= 11011011;
小數:
X = 0.1011,原碼:[X]原 = 0.1011;
X = -0.1011,原碼:[X]原 = 1.1011;
當采用原碼表示法時,編碼簡單直觀,與真值轉換方便。但原碼也存在一些問題,一是零的表示不唯一,因為:[+0] = 000···0,[-0] = 100···0 零有二義性, 給機器判零帶來麻煩。二是用原碼進行四則運算時,符號位需要單獨處理,且運算規則復雜。例如加法運算,若兩數同號,兩數相加,結果取共同的符號; 若兩數異號,則要大數減去小數,結果冠以大號的符號。此外,借位操作如果用計算機硬件來實現是很困難的。
2.反碼
反碼很少使用,但作為一種編碼方式和求補碼的中間碼,我們還是要學習一下的。
正數的反碼與原碼表示相同。
負數反碼的符號位與原碼相同(仍用1表示),其余各位取反(0變1,1變0)。例如:
X = +1100110, [X]原 = 01100110,[X]反 = +1100110;
X = -1100110,[X]原 = 11100110,[X]反 = 10011001;
X = +0000000,[X]原 = 00000000,[X]反 = 00000000;
X = -0000000,[X]原 = 10000000,[X]反 = 111111111;
和原碼一樣,反碼中的零的表示也不唯一。
當X為純小數時,反碼的表示如下:
X = 0.1011,[X]原 = 0.1011,[X]反 = 0.1011;
X = -0.1011,[X]原 = 1.1011,[X]反 = 1.0100;
3.補碼
(1)模數的概念
模數從物理意義上講,是某種計量器的容量。例如,我們日常生活中用到的鐘表,模數就是12。鐘表計時的方式就是達到12就從零開始(扔掉一個12),這在數學上是一種"取模(或取余)運算(mod)"。例如:14%12 = 2。
如果現在的準確時間是6點整,而你的手表指向8點,怎樣把表撥準呢?可以有兩種方法:把表往後撥2小時,或把表往前撥10小時,效果是一樣的。即:
8-2 = 6
(8+10)%12 = 6
在模數系統中,8-2 = 8+10 (mod 12)
上式之所以成立,是因為2與10對模數12是互為補數的(2+10 = 12)。因此,可以認可這樣一個結論:在模數系統中,一個數減去另一個數,或者一個數加上一個負數,等於第一個數加上第二個數的補數:
8+(-2) = 8+10 (mod 12)
我們稱10為-2在模12下的"補碼"。負數采用補碼表示後,可以使加減法統一為加法運算。
在計算機中,機器表示數據的字長是固定的。對於n位數來說,模數的大小是:n位數全為1,且末位再加1。實際上模數的值已經超過了機器所能表示的數的範圍,因此模數在機器中是表示不出來的。若運算結果大於模數,則模數自動丟掉,也就等於實現了取模運算。
如果有n位整數(包括一位符號位),則它的模數為2^n;如果有n位小數,小數點前一位為符號位,則它的模數為2。
***(2)補碼表示法***
由以上討論得知,對於一個二進制負數,可用其模數與真值做加法(模減去該數的絕對值)求得其補碼。
例: X = -0110 [X]補 = 2^4 + (-0110) = 1010
X = -0.1011 [X]補 = 2 + (-0.1011) = 1.0101
由於機器中不存在數的真值形式,用上述公式求補碼在機器中不易實現,但從上式可推導出一個簡便方法。
"對於一個負數,其補碼由該數反碼的最末位加1求得。"
"對於正數來說,其原碼、反碼、補碼形式相同。”
補碼的特點之一就是零的表示唯一。
[+0]補 = 0 0 ···0 [-0]補 = 1 1 ··· 1 + 1 = 1 0 0 ··· 0
|_____| |______| | |______|
n位 n位 | n位
|_ _ _ _ _ _ _ 自動丟失
這種簡便的求補碼方法經常被簡稱為"求反加1"。
(3)補碼的運算規則
采用補碼表示的另一個好處就是當數值信息參與算術運算時,采用補碼方式是最方便的。首先,"符號位可作為數值參加運算,"最後仍可得到正確的結果符號,符號無需單獨處理;其次,采用補碼進行運算時,減法運算可轉換為加法運算,簡化了硬件中的運算電路。
例如:
計算67-10=?
[+67]原 = 01000011, [+67]補 = [+67]原
[-10]原 = 10001010, [-10]補 = 11110110
0 1 0 0 0 0 1 1 [+67]補
+ 1 1 1 1 0 1 1 0 [-10]補
——————————————
1 0 0 1 1 1 0 0 1 = 57
|____________________________ 最高位的進位自然丟失
由於字長只有8位,因此加法最高位的進位自然丟失,達到了取模效果(即丟掉了一個模數)。
應當指出,"補碼運算的結果仍為補碼。" 上例中,從結果符號位得知,結果為正,所以補碼即原碼,轉換成十進制數為57。
如果結果為負,則是負數的補碼形式,若要變成原碼,需要對補碼再求補,即可還原為原碼。
例如:
10 - 67 = ?
[+10]原 = 00001010 = [+10]補
[-67]原 = 11000011 [-67]補 = 10111101
0 0 0 0 1 0 1 0
+ 1 0 1 1 1 1 0 1
——————————
1 1 0 0 0 1 1 1
[結果]補 = 11000111,[結果]原 = 10111001
所以結果的真值為-0111001,十進制為-57。
以上兩個例子是否就可以說明補碼運算的結果總是正確的呢?請看下面的例子:
例如:
85 + 44 = ?
0 1 0 1 0 1 0 1
+ 0 0 1 0 1 1 0 0
———————————
1 0 0 0 0 0 0 1
從結果的符號位可以看出,結果是一負數。但兩個整數相加不可能是負數,問題出在什麽地方呢?原來這是由於"溢出"造成的,即結果超出了一定位數的二進制數所能表示的數的範圍。
以上內容節選自清華大學出版社《C++語言程序設計(第4版)》
二進制數的編碼表示