引子
這幾天在看HashMap的源碼,發現其源碼中有一些非常巧妙的位運算,而位運算又牽涉到原碼、反碼、補碼等計算機信息的表示。所以,我就在這裏系統地總結一下這部分的知識,一來加深自己的記憶,而來希望可以幫到大家。
信息的表示
本節中的範例大多來自於《C++程序語言設計》這本書,各位可以去參考一下~
二進制
幾乎所有的計算機都採用的是二進制數系。當數據被轉換爲二進制數後,計算機才能對其進行處理。當然,還有一些常見的進位數制我們也需要了解:
進 制 | 基數 | 進位原則 | 基本符號 |
---|---|---|---|
二進制 | 2 | 逢 2 進 1 | 0,1 |
八進制 | 8 | 逢 8 進 1 | 0,1,2,3,4,5,6,7 |
十進制 | 10 | 逢 10 進 1 | 0,1,2,3,4,5,6,7,8,9 |
十六進制 | 16 | 逢 16 進 1 | 0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F |
採用二進制碼錶示信息,有如下幾個優點:
- 易於物理實現
具有兩種穩定狀態的物理器件是很多的:如門電路的導通與截止,電壓的高與低。他們都恰好可以對應 0 和 1 兩個符號。假如採用十進制,要製造具有10種穩定狀態的物理電路,那是非常困難的。
- 二進制數運算簡單
經過數學推導可以證明:對R進制的算數求和、求積規格各有 R(R+1)/2 種。如果採用十進制,就有 55 中求和與求積的運算規則;而二進制僅有3種,因此就簡化了運算器等物理器件的設計。
- 機器可靠性高
由於電壓的高低、電流的有無 等都是一種質的變化,兩種狀態分明。所以二進制編碼的抗干擾能力強,鑑別信息的可靠性高。
- 通用性強
二進制編碼不僅可以表示數值,而且還可以用於 非數值信息的編碼。比如,布爾值 True、False 就可以分別和 1、0 對應,從而爲計算機實現邏輯運算與判斷提供了方便。
當然,二進制編碼也有一些不足:它表示數的容量最小;表示同一個數,二進制比其他進制需要更多的位數。
進制轉換
這一部分,我直接給出幾個進制轉化的例子,大家一看應該就能明白。
R進制轉十進制
如下圖,下面是最常見的 2進制 轉 10進制:
如圖,第二個例子是 帶有小數的二進制數 轉換爲 十進制數。大家一看應該就能明白。
上面兩個例子分別是 八進制、十六進制的數轉換爲 十進制數。這部分很簡單,不多加闡述。
十進制轉R進制
關於十進制數轉換爲R進制的數,這裏分爲兩部分說明:整數的轉換 與 小數的轉換。
整數轉換:
十進制的整數轉換爲R進制的整數,採用的是“除R取餘法”。下面看例子:
也就是說,。再看一個例子,將 轉換爲 八進制數,採用除8取餘:
也就是說, 。其他的進制轉化也是類似,十進制轉十六進制時,採用 除16取餘 即可。這裏就不多加介紹了。
小數轉換:
十進制整數轉換爲R進制的整數時,採用的方法是“除R取餘”。當需要轉換的十進制數包含小數位數時,可以分爲兩部分處理:整數部分,處理方法同上,採用“除R取餘”;小數部分,採用“乘R取整”。下面看例子:
將 轉換爲 二進制數:
如上圖,我們將小數部分依次乘以基數,轉換爲R進制就乘以R。依次乘下去,直到小數部分爲0,或者達到所需要的精度爲止(小數部分可能永遠都不爲0。也就是說,十進制小數往往不能精確地轉化爲等值的R進制小數)。最終得到結果: 。
我們已經知道,也知道,那也就是說:。將整數部分和小數部分拼接起來即可。
二、八、十六進制間的轉換
二、八以及十六進制 三種進制的數之間存在一些內在聯繫:每一位八進制數相當於3位二進制數(),每一位十六進制的數相當於4位二進制數()。
二進制數,從小數點開始,向左右分別按三(四)位爲一個單元劃分,每個單元單獨轉換成爲一個八進制(十六進制)的數,就完成了二進制到八進制(十六進制)的轉換。在轉換時,位組的劃分是以小數點爲中心向兩邊延伸,中間的 0 不能省略,兩頭不夠時可以補 0 。
八進制(十六進制)的每一位,分別獨立轉換成三位(四位)二進制數,除了左邊的最高位,其他位如果不足三位(四位)的要用 0 來補足,按照由高位到低位的順序寫到一起,就是相應的二進制數。
下面看幾個例子:
上圖多看幾遍,也是很容易理解的。
信息的存儲
在計算機內部,各種信息都是以二進制編碼形式進行存儲。
信息存儲的單位
信息存儲的單位通常採用“位”、“字節”和“字”。
- 位:bit,度量數據的最小單位,表示1位二進制信息。
- 字節:Byte,一個字節由 8 位二進制數字組成(1 Byte = 8bit)。字節是信息存儲中最常用的基本單位。計算機的存儲器通常也是以多少字節來表示它的容量。
- 字:Word,字是位的組合,並作爲一個獨立的信息單位處理。字又稱爲“計算機字”,它的含義取決於機器的類型、字長以及使用者的要求。常用的固定字長有 8位、16位、32位等。
- 機器字長:在討論信息單位時,還有一個與機器硬件指標有關的單位,這就是機器字長。機器字長一般是指參加運算的寄存器所含有的二進制數的位數,它代表了機器的精度,如 32位、64位等。
二進制數的編碼表示
數值在計算機內是採用二進制編碼表示。數有正負之分,一般情況下,在計算機中使用“0”表示正號,“1”表示負數。符號位放在數的最高位。例如,8位二進制數 它們在機器中可以表示爲:
其中最左邊一位代表符號位,連同數字本身一起作爲一個數。
人們研究了符號數的多種二進制編碼方法,其實質是對負數表示的不同編碼。下面介紹一下幾種常見的編碼:原碼、反碼 和 補碼。
注意:在下面的示例中,使用 一個字節存放一個整數,所有整數使用8位二進制碼錶示!
原碼
將 符號 數字化爲 0 或 1,數的絕對值與符號一起編碼,這樣的二進制碼稱爲“原碼”。如上面編碼後的A、B,得到的二進制碼都是原碼。
對於一個整數,編碼方式就是上面例子中編碼A、B那樣,用 0、1分別代表 正好、負號。例如:
對於一個帶符號的純小數,它的原碼錶示就是把小數點左邊一位用作符號位。例如:
採用原碼錶示法時,編碼簡單直觀,與原始值(真值)轉換方便。但是也有一些缺點:
- 零(0)的表示不唯一。
- 另一個原因是,使用原碼進行四則運算時,符號位需要單獨處理,並且運算規則複雜。
反碼
反碼很少使用,這這裏講它主要是因爲 反碼是求補碼的中間碼。
對於整數而言,正數的反碼與原碼錶示相同;負數反碼的符號位與原碼相同,仍用“1”表示,其餘各位取反,0變成1,1變成0。
很顯然,和原碼一樣,反碼中,0的表示也不唯一。
對於純小數而言,反碼也類似,小數點左邊的符號位不變,小數點右邊的小數位取反。
補碼
對於一個正數來說,其原碼、反碼、補碼 表示相同。對於一個負數,其補碼由該數反碼最末位加 1 求得。
對於一個純小數,反碼的求法一樣:
從上面的例子中可以看出,零(0)的補碼錶示是唯一的。採用補碼錶示的另一個好處就是,當數值信息參與算數運算時,採用補碼的方式是最簡便的:一是因爲符號位可以作爲數值參加運算,最後仍然可以得到正確的符號,無需單獨處理;二是採用補碼進行運算時,減法運算可以轉化爲加法運算,簡化了硬件中的運算電路。正是由於這些優點,在計算機系統中,數值都採用補碼來表示和存儲。
需要注意的是,補碼的運算結果仍然是補碼。下面看兩個例子:
例一:求 67 - 10 = ?
如上圖,67 與 -10 的補碼如圖,則有:
計算結果記爲R,也就是說,R的補碼爲 00111001。顯然R是個正數,也就是說,R的原碼也爲 00111001。則R = 57。
例二:求10 - 67 = ?
如上圖,我們計算出 +10 與 -67 的補碼,然後計算:
結果記爲R,則R的補碼爲 11000111,從符號位看,R是個負數。要計算結果值,則我們要將結果的補碼轉換爲原碼:只需要對補碼再求補碼,即可還原爲原碼。
計算得出,R的原碼爲 10111001,則 R = -57。
總結
技術更新換代太快,但是計算機總是圍繞着一些基本理論在發展。所以,我們在瘋狂學習新技術時,也應該好好掌握好這些基礎知識。技術變化很快,然而萬變不離其宗。
參考文檔
1、《C++語言程序設計》第四版,鄭莉·著。第一章:緒論
2、《Java編程思想》第四版,Bruce Eckel·著。第三章:操作符