一、二進制前置知識
1.十進制和二進制的轉換
十進制轉換二進制----除基取余法
例如:(55)轉換為二進制
用55除2,得到27余1,------1
再用27除2,得到13余1,------1
再用13除2,得到6余1,------1
再用6除2,得到3余0,------0
再用3除2,得到1余1,------1
再用1除2,除不了余1。------1
把余數從下往上取,即 110111 = 55
二進制轉十進制----位權法
例如:(110111)轉換為十進制
二、硬件知識
任何數據都是以二進制存儲在計算機中。
根據馮·諾依曼提出的經典計算機體系結構框架,一台計算機由運算器、控制器、存儲器、輸入和輸出設備組成。其中,運算器只有加法運算器。
計算機雖然沒辦法做減法,但是可以加上這個數的相反數呀。但是二進制我們不能給它加上負號,於是我們需要引入一個符號位,存儲在最左邊的一位上,0代表正數,1代表負數。例如一個四位二進制數,最左邊是符號位。0001,表示他是+1,1001,表示他是-1。
三,原碼
終於到正題了,想象你是當年設計計算機的科研人員。你把帶符號位的四位二進制數做運算,向全世界展示你精妙絕倫的設計--符號位。
於是你開始了計算----
- 0001+0010=0011 1+2=3
- 1000+0000=1000 (-0)+0=(-0)
- 0101+1010=1111 5+(-2)=-7
你突然發現,正數加正數沒有問題,但是后面的測試出現了問題
1:怎么會有兩個0呢?
因為1000和0000都表示 零
2:怎么正數加負數會出現問題呢?
因為符號位引起的。
總結一下:
1、原碼直觀
2、正數相加沒問題
3、0有兩種表達,運算時需要將-0轉換為0,也就是1000轉換為0000
四、反碼
正數的反碼不變。
負數的反碼:符號位不變,將原碼取反。
例如:
原碼000的反碼就是0110
然后我們試一試用反碼計算一下剛才沒有解決的問題
- 0010 + 1101 = 1111 1+(-1)=0 把1111取反碼,1000,也就是說兩個相反數相加取反碼結果沒有問題
- 1110 + 1011 = 1001 (-6)+(-3)=(-6)取反得到1110, 這樣算出來的是-6,出錯了
總結一下:
1、反碼在計算相反數相加的時候不會出錯(計算的時候會遇到運算位溢出,此時直接忽略高位即可)
2、0還是有兩種表達0000和1111
3、容納數字的范圍和原碼相同(如上圖,原碼與反碼集合存在映射關系)
4、往往是中間變換量,不會直接用
五、補碼
正數的補碼不變。
負數的補碼等於反碼+1。
我們需要注意的是,目前大多數書籍只介紹了補碼如何計算,但是都沒有講清楚為何是這樣計算,以及這樣計算的依據。
筆者在初學的時候也遇到了很多疑惑。關於補碼的嚴格定義,可以自行百度,里面會介紹模與同余數的概念。
在此簡單介紹一下,舉一個生活中常見的12進制的例子來說明模的概念:
假如當前有一個時鍾,指針指向9,如果我要調到12點,我們有兩個方法,+3或者-9.
也就是說凡是-9的運算都可以看做是+3的運算。
這個情況下,模是12(mod)
思考到這里時請放緩腳步,慢慢思考。
我們會發現,9+3居然和9-9在某種意義上的效果是一樣的。
既然兩者效果相同那么一個數a減去一個數b就相當於加上這個數b的同余數。
於是推出模的一般公式:a-b=a-b+mod=a+mod-b
利用這個思想,我們把它帶到二進制的世界里(假設是四位的二進制)
我們試圖運算0011 - 0010 = 0001,但是我們發現計算機中沒有減法器,不能算。
我想便利用上面的思想,減去一個數等於加上一個數的同余數,也就是0011 加上0010的同余數即可
四位二進制的模 10000,那么0010的同余數就是10000-0010=1110,
那么我們就直接0011+1110=10001,但是我們是四位的運算,多出的一位會被直接舍棄,計算機會把多出來的一位放在psw寄存器中,不討論。
那么至此,我們就可以利用補碼計算加法和減法了。
想必你有疑問,怎么求一個數的補碼呢?我們經過大量計算發現,補碼竟然是原碼的反碼+1,非常不可思議。
為什么會這樣呢?
六、總結與原理
因為負數的反碼加上這個負數的絕對值正好等於1111,在加1,就是10000,也就是四位二進數的模,而負數的補碼是它的絕對值的同余數,可以通過模減去負數的絕對值得到它的補碼,所以負數的補碼就是它的反碼+1。
參考文獻:Programming in C