補碼
所有的計算機資料都規定:補碼是將各位按位取反,再加1.
學的時候並沒有覺得有什么疑問,但是寫代碼很多年之后,回過頭去溫習計算機原理,突然冒出來幾個問題
為什么要有補碼這個東西?為什么補碼要按位取反?為什么要有左移右移?
這些都是為了計算機實現加減乘除才出現的。首先講講加減,計算機沒有減法,所以就是加上負數來替代它。
舉個例子吧 2的二進制是0000 0010,那么-2的表示是否是1000 0010呢 驗證一下,假如它們成立就必須2+(-2)=0 兩者的相加為1000 0100,不管前面符號位是0還是1,這個數都不等於0.
那么我們反過來想,我想要獲取一個數的負數是多少,那么只要確保這個數加上它的負數等於0
還是剛剛的例子 2的二進制是0000 0010,那么0的二進制是0000 0000 我們知道二進制加法中,正常情況下2加上任何數都不能等於0,只有一種情況,那就是高位溢出。比如2加上某個數變成了1 0000 0000,這個時候計算機會棄掉高位的1. 這就很簡單了,我把2按位取反,變成1111 1101,它加上2就是1111 1111,這個時候我只需要再加1,就能變成1 0000 0000. 所以將2按位取反再加1為 1111 1110,這就是補碼。
補碼的規則就有了,將各位按位取反,再加1. 補碼就是用正數表示的負數形式。
因為符號位的特殊,0000 0000 - 1111 1111 一共能表示256個,所以1個字節表示的無符號范圍是0 到28 -1(一共256個),有符號- 27 到 27-1(一共256個)
好,問題來了,無符號的范圍不用說,有符號的范圍為什么負數是-128,正數只能到127呢
我們可以看到,有符號位的二進制數第一位是符號,所以剩下的7位表示數 正數范圍從0到127沒有問題。 1到127對應的數都有其補碼,對應了-127到-1 唯獨剩下個0,0歸為正數,它的二進制是0000 0000,那么現在就剩下一個1000 0000沒有表示,因為符號位的原因,它相當於是-0.但是-0也是0,所以規定1000 0000表示負數最大值,也就是128
所以0的補碼就是-128,所以有符號位的范圍是-128到127
左移和右移
補碼的出現是為了表示負數,來做加法和減法。那么左移和右移就是來做乘法以及除法。
左移是指把所有的位向左移動n位數,那么這個數就擴大了2n倍
左移是指把所有的位向又移動n位數,那么這個數就縮小了2n倍
這樣我們就能實現乘法和除法,比如5*3就變成了5*(2+1)就等於5*21+5,也就是將5左移1位,再加上5。
除法假如除數是2的冪次方,比較簡單,右移2的冪次位就行,比如5/2,只要把5右移1位得到整數位是2.
那么小數位呢?(小數位比較復雜,這邊就不討論了)假如除數不是2的冪次方呢?
x/y其實就是,x不斷減y的過程。小學時候學的長長除法就是這個原理。 用二進制的除法x/y,比十進制容易寫,商不是0即是1,而且如果除數大於除數的1倍,商就是標記在另一個位上面了 二進制除法x/y=0.1001/0.1011手工計算如下 0.11 _______ 0.1001/0.1001 10010(后面補0) -1011 ------ 111(余數) 1110(后面補0) -1011 -------- 1(余數) 設ri表示第i次運算后所得的余數,則: 若ri>0,則商1,余數和商左移1位,再減去除數,即ri+1=2ri-y 若ri<0,則商0,余數和商左移1位,再加上除數,即ri+1=2ri+y 用85/6來舉例,85/6=1010101/110 a.101(0101)左移1位到第3位都小於110,因此商=000 b.1010(101)左移四位是1010,比110大,商=0001,余數=1010-110=100(101) c.余數100(101)左移一位是1001,比110大,商=00011,余數=1001-110=11(01) d.余數11(01)左移一位是110,等於110,商=000111,余數=0(1) e.余數0(1)左移一位是01,小於110,商=0001110,余數=01 因此85/6=1010101/110=0001110,即14,余數為最后的余數1
無符號數的左移和右移是沒有問題的,往左移相當於增加2倍,往右移是減小2倍,
但是假如是有符號數呢,第一位是符號位,那么直接把后面的7位左移或者右移就可以了嗎?
先來看左移
以-2為例 2轉換成二進制是0000 0010 2的補碼是 1111 1110 對1111 1110左移一位得到1111 1100 將補碼-1得到1111 1011,再按位取反得到正數是0000 0100,是4 左移1位相當於是乘以2
所以有符號數的左移也是相當於增加2的冪次方倍
下面看右移,這就涉及到2種方式:
邏輯右移:把所有的位,包括符號位向左或向右移動,高位補0
算術右移:假如是正數,高位補零,假如是負數,高位補1。
看圖中的例子,邏輯右移不能得到正確答案,算數右移才是正確的
還是以-4為例子, 我們先看下它的正數的例子 原始數值 0000 0100 右移兩位 0000 0001 最終結果 1 我們還是按結果倒推,-4右移兩位我們想要的結果是-1 那么以-1為例,左移兩位不就是-4嗎 原始數值 1111 1111 左移兩位 1111 1100 根據補碼的規則,把正數的各位取反加1. 設想一下假如負數右移的過程中把高位取0,那么減去1之后,把各位再取反得到得正數,它得高位將會是1,這顯然是不對得。
所以負數得右移應該以算數右移為准。