程序是怎樣跑起來的--二進制基礎運算

一、什麼是二進制數

 爲了更清晰地說明二進制數的機制,首先讓我們把00100111這個二進制數值轉換成十進制數值來看一下。二進制數的值轉換成十進制數的值,只需將二進制數的各數位的值和位權相乘,然後將相乘的結果相加即可。

首先,讓我們從位權的含義說起。例如,十進制數39的各個數位的數值,並不只是簡單的3和9,這點大家應該都知道。3表示的是3×10=30,9表示的是9×1=9。這裏和各個數位的數值相乘的10和1,就是位權。數字的位數不同,位權也不同。第1位(最右邊的一位)是10的0次冪(=1),第2位是10的1次冪(=10),第3位是10的2次冪(=100),依此類推。這部分相信大家都能夠理解。那麼,我們就繼續講一下二進制數。

位權的思考方式也同樣適用於二進制數。即第1位是2的0次冪(=1),第2位是2的1次冪(=2),第3位是2的2次冪(=4), ……,第8位是2的7次冪(=128)。“〇〇的××次冪”表示位權,其中,十進制數的情況下〇〇部分爲10,二進制數的情況下則爲2。這個稱爲基數。十進制數是以10爲基數的計數方法,二進制數則是以2爲基數的計數方法。“〇〇的××次冪”中的××,在任何進制數中都是“數的位數-1”。即第1位是1- 1=0次冪,第2位是2- 1=1次冪,第3位是3- 1=2次冪。

接下來,讓我們來解釋一下各數位的數值和位權相乘後“相加”這個處理的原因。其實大家所說的數值,表示的就是構成數值的各數位的數值和位權相乘後再相加的結果。例如39這個十進制數,表示的就是30+9,即各數位的數值和位權相乘後再相加的數值。這種思考方式在二進制數中也是通用的。二進制數00100111用十進制數表示的話是39,因爲(0×128)+(0×64)+(1×32)+(0×16)+(0×8)+(1×4)+(1×2)+(1×1)=39。

二、移位運算和乘除運算的關係

在瞭解了二進制數的機制後,接下來我們來看一下運算。和十進制數一樣,四則運算同樣也可以使用在二進制數中,只要注意逢2進位即可。下面,我們就來重點看一下二進制數所特有的運算。二進制數所特有的運算,也是計算機所特有的運算,因此可以說是瞭解程序運行原理的關鍵。首先我們來介紹移位運算。移位運算指的是將二進制數值的各數位進行左右移位(shift=移位)的運算。移位有左移(向高位方向)和右移(向低位方向)兩種。在一次運算中,可以進行多個數位的移位操作。代碼清單2-1中列出的是把變量a中保存的十進制數值39左移兩位後再將運算結果存儲到變量b中的C語言程序。<<這個運算符表示左移,右移時使用>>運算符。<<運算符和>>運算符的左側是被移位的值,右側表示要移位的位數。那麼,這個示例程序運行後,變量b的值是多少?

代碼清單2-1 將變量a的值左移兩位的C語言程序

a = 39;
b = a << 2;

如果你認爲“由於移位運算是針對二進制數值的位操作,十進制數39的移位操作就行不通了”,那麼就請重新讀一下本章的內容。無論程序中使用的是幾進制,計算機內部都會將其轉換成二進制數來處理,因此都能進行移位操作。但是,“左移後空出來的低位,要補上什麼樣的數值呢?空出來的低位要進行補0操作。不過,這一規則只適用於左移運算。至於右移時空出來的高位要進行怎樣的操作,我們會在後面說明。此外,移位操作使最高位或最低位溢出的數字,直接丟棄就可以了。接下來讓我們繼續來看代碼清單2-1。十進制數39用8位的二進制表示是00100111,左移兩位後是10011100,再轉換成十進制數就是156。不過這裏沒有考慮數值的符號。

實際的程序中,移位運算以及將在本章最後介紹的邏輯運算在使用位單位處理信息的情況下比較常用。雖然這裏沒有列舉具體的程序示例,但對程序員來說,掌握位運算和邏輯運算的機制是一項基本技能,所以一定要掌握。形象地說,移位運算就好比使用二進制表示的圖片模式像霓虹燈一樣左右流動的樣子。不過,移位運算也可以通過數位移動來代替乘法運算和除法運算。例如,將00100111左移兩位的結果是10011100,左移兩位後數值變成了原來的4倍。用十進制數表示的話,數值從39(00100111)變成了156(10011100),也正好是4倍(39×4=156)。

其實,反覆思考幾遍後就會發現確實如此。十進制數左移後會變成原來的10倍、100倍、1000倍……同樣,二進制數左移後就會變成原來的2倍、4倍、8倍……反之,二進制數右移後則會變成原來的1/2、1/4、1/8……這樣一來,大家應該能夠理解爲什麼移位運算能代替乘法運算和除法運算了吧。

三、便於計算機處理的“補數”

剛纔之所以沒有介紹有關右移的內容,是因爲用來填充右移後空出來的高位的數值,有0和1兩種形式。要想區分什麼時候補0什麼時候補1,只要掌握了用二進制數表示負數的方法即可。這部分內容較多,接下來我們就一起來看看錶示負數的方法和右移的方法。二進制數中表示負數值時,一般會把最高位作爲符號來使用,因此我們把這個最高位稱爲符號位。符號位是0時表示正數,符號位是1時表示負數。那麼-1用8位二進制數來表示的話是什麼樣的呢?可能很多人會認爲“1的二進制數是00000001,因此-1就是10000001”,但這個答案是錯的,正確答案是11111111。計算機在做減法運算時,實際上內部是在做加法運算。用加法運算來實現減法運算,是不是很新奇呢?爲此,在表示負數時就需要使用“二進制的補數”。補數就是用正數來表示負數,很不可思議吧。

爲了獲得補數,我們需要將二進制數的各數位的數值全部取反,然後再將結果加1。例如,用8位二進制數表示- 1時,只需求得1,也就是00000001的補數即可。具體來說,就是將各數位的0取反成1, 1取反成0,然後再將取反的結果加1,最後就轉化成了11111111。

補數的思考方式,雖然直觀上不易理解,但邏輯上卻非常嚴謹。例如1-1,也就是1+(- 1)這一運算,我們都知道答案應該是0。首先,讓我們將-1表示成10000001(錯誤的表示方法)來運算,看看結果如何。00000001+10000001=10000010,很明顯結果不是0。如果結果是0,那麼所有的數位都應該是0纔對。

接下來,讓我們把- 1表示成11111111(正確的表示方法)來進行運算。00000001+11111111確實爲0(=00000000)。這個運算中出現了最高位溢出的情況,不過,正如之前所介紹的那樣,對於溢出的位,計算機會直接忽略掉。在8位的範圍內進行計算時,100000000這個9位二進制數就會被認爲是00000000這一8位二進制數。

補數求解的變換方法就是“取反+ 1”。爲什麼使用補數後就能正確地表示負數了呢?爲了加深印象,我們來看一上圖,與此同時也希望大家能夠牢記“將二進制數的值取反後加1的結果,和原來的值相加,結果爲0”這一法則。首先,大家可以用1和-1的二進制形式,來徹底地瞭解補數的相關內容。除了1+ (- 1)之外,2+ (- 2)、39+ (- 39)等同樣如此。總之,要想使結果爲0,就必須通過補數來實現。當然,結果不爲0的運算同樣可以通過使用補數來得到正確的結果。不過,有一點需要注意,當運算結果爲負數時,計算結果的值也是以補數的形式來表示的。比如3- 5這個運算,用8位二進制數表示3時爲00000011,而5=00000101的補數爲“取反+ 1”,也就是11111011。因此3- 5其實就是00000011+ 11111011的運算。

00000011 + 11111011的運算結果爲11111110,最高位變成了1。這就表示結果是一個負數,這點大家應該都能理解。那麼11111110表示的負數是多少大家知道嗎?這時我們可以利用負負得正這個性質。假若11111110是負△△,那麼11111110的補數就是正△△。通過求解補數的補數,就可知該值的絕對值。11111110的補數,取反加1後爲00000010。這個是2的十進制數。因此,11111110表示的就是- 2。我們也就得到了3- 5的正確結果。

編程語言包含的整數數據類型[插圖]中,有的可以處理負數,有的則不能處理。例如,C語言的數據類型中,既有不能處理負數的unsigned short類型,也有能處理負數的short類型。這兩種類型,都是2字節(=16位)的變量,都能表示2的16次冪=65536種值,這一點是相同的。不過,值的範圍有所不同,short類型是- 32768~32767, unsigned short類型是0~65535。此外,short類型和unsigned short類型的另一個不同點在於,short類型是將最高位爲1的數值看作補數,而unsigned short類型則是32768以上的值。仔細思考一下補數的機制,大家就會明白像- 32768~32767這樣負數比正數多一個的原因了。最高位是0的正數,有0~32767共32768個,這其中也包含0。最高位是1的負數,有- 1~- 32768共32768個,這其中不包含0。也就是說,0包含在正數範圍內,所以負數就要比正數多1個。雖然0不是正數,但考慮到符號位,就將其劃分到了正數中。

四、邏輯右移和算術右移的區別

在瞭解了補數後,讓我們返回到右移這個話題。前文已經介紹過,右移有移位後在最高位補0和補1兩種情況。當二進制數的值表示圖形模式而非數值時,移位後需要在最高位補0。類似於霓虹燈往右滾動的效果。這就稱爲邏輯右移。

將二進制數作爲帶符號的數值進行運算時,移位後要在最高位填充移位前符號位的值(0或1)。這就稱爲算術右移。如果數值是用補數表示的負數值,那麼右移後在空出來的最高位補1,就可以正確地實現1/2、1/4、1/8等的數值運算。如果是正數,只需在最高位補0即可。現在我們來看一個右移的例子。將- 4(=11111100)右移兩位。這時,邏輯右移的情況下結果就會變成00111111,也就是十進制數63,顯然不是- 4的1/4。而算術右移的情況下,結果就會變成11111111,用補數表示就是- 1,即- 4的1/4。

只有在右移時才必須區分邏輯位移和算術位移。左移時,無論是圖形模式(邏輯左移)還是相乘運算(算術左移),都只需在空出來的低位補0即可。下面順便介紹一下符號擴充。以8位二進制數爲例,符號擴充就是指在保持值不變的前提下將其轉換成16位和32位的二進制數。將01111111這個正的8位二進制數轉換成16位二進制數時,很容易就能得出0000000001111111這個正確結果,但是像11111111這樣用補數來表示的數值,該如何處理比較好呢?實際上處理方法非常簡單,將其表示成1111111111111111就可以了。也就是說,不管是正數還是用補數表示的負數,都只需用符號位的值(0或者1)填充高位即可。這就是符號擴充的方法。下向我們展示了將符號位擴充到高位的具體流程。

五、掌握邏輯運算的竅門

解釋邏輯右移時,提及了“邏輯”這個術語。大家聽到邏輯這個詞可能會感覺有些難,但實際上它很簡單。在運算中,與邏輯相對的術語是算術。我們不妨這樣考慮,將二進制數表示的信息作爲四則運算的數值來處理就是算術。而像圖形模式那樣,將數值處理爲單純的0和1的羅列就是邏輯。計算機能處理的運算,大體可分爲算術運算和邏輯運算。算術運算是指加減乘除四則運算。邏輯運算是指對二進制數各數字位的0和1分別進行處理的運算,包括邏輯非(NOT運算)、邏輯與(AND運算)、邏輯或(OR運算)和邏輯異或(XOR運算)四種。

邏輯非指的是0變成1、1變成0的取反操作。邏輯與指的是“兩個都是1”時,運算結果爲1,其他情況下運算結果都爲0的運算。邏輯或指的是“至少有一方是1”時,運算結果爲1,其他情況下運算結果都是0的運算。邏輯異或指的是排斥相同數值的運算。“兩個數值不同”,也就是說,當“其中一方是1,另一方是0”時運算結果是1,其他情況下結果都是0。不管是幾位的二進制數,在進行邏輯運算時,都是對相對應的各數位分別進行運算。表2-1~表2-4總結了各邏輯運算的結果。這些表稱爲真值表。如果將二進制數的0作爲假(false)、1作爲真(true)來考慮,邏輯運算也可以被認爲是真假的運算。真和真的AND運算結果爲真,實際上也確實如此。因爲如果兩方面都是真,答案就是真。

 

 

 

 

掌握邏輯運算的竅門,就是要摒棄用二進制數表示數值這一想法。大家不要把二進制數表示的值當作是數值,而應該把它看作是圖形或者開關上的ON/OFF(1是ON,0是OFF)。邏輯運算的運算對象不是數值,因此不會出現進位的情況。看起來好像有些麻煩,總之就是不要將它作爲數值來考慮。另外,還有一點非常重要,就是要對各種邏輯運算分別能實現什麼有一個整體印象。形成這樣的印象後,即使不看真值表也能判斷出運算的結果。下圖表示的是對NIKKEI的頭兩個字母NI這一圖形模式進行各種邏輯運算後的結果。假設白色部分表示1,黑色部分表示0。通過下圖,我們就會對邏輯運算有一個具體的把握,即“邏輯非是所有位的取反操作”“邏輯與是將一部分變爲0(復位到0)的操作”“邏輯或是將一部分變爲1(復位到1)的操作”“邏輯異或是將一部分進行取反(相同取0,不同取1)的操作”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章