博客地址:https://www.cnblogs.com/jackieL/算法
做者: 梁言 spa
時間:2019年2月19日指針
最近在網上查了不少關於補碼的文章,要麼是長篇大論,要麼就是錯誤百出,因此我用簡單的語言把這個問題分析一遍,以便於你們理解記憶,若有錯誤歡迎留言指正。blog
一,「原碼」、「反碼」、「補碼」的基本概念同步
針對還不明白這幾個基礎概念的同窗們須要闡述一下,若是已經知道的同窗自行跳過。博客
一、「原碼」數學
就是二進制定點表示法,即最高位爲符號位,「0」表示正,「1」表示負,其他位數表示數字的值。it
如:十進制 6 = 二進制原碼 0000 0110 基礎
十進制 -6 = 二進制原碼 1000 0110循環
二、「反碼」
正數的反碼與其原碼相同,負數的反碼是對其原碼逐位取反,符號位除外。
如:十進制 6 = 二進制原碼 0000 0110 = 二進制反碼 0000 0110
十進制 -6 = 二進制原碼 1000 0110 = 二進制反碼 1111 1001
三、「補碼」
正數的補碼與原碼相同,負數的補碼是在其反碼的末位加1。
如:十進制 6 = 二進制原碼 0000 0110 = 二進制反碼 0000 0110 = 二進制補碼 0000 0110
十進制 -6 = 二進制原碼 1000 0110 = 二進制反碼 1111 1001 = 二進制補碼 1111 1010
2、「原、反、補」的產生緣由
由於計算機cpu只有加法運算器,沒有減法運算器,因此須要把減法轉換爲加法來作,因此天然就產生了「原、反、補」來將減法轉換爲加法計算。
雖然這個東西有點反人類,但畢竟是機器去使用它,咱們只須要明白就行。
3、「反碼」應運而生
二進制「反碼」很容易產生,由於cpu有一個二進制取反邏輯門電路,只要把「原碼」經過那個「門」出來就變成「反碼」了
「原碼」與「反碼」有一個特色:
以一個字節二進制數據爲例
十進制 -6 = 二進制原碼 1000 0110 = 二進制反碼 1111 1001
任意一個數的「原碼」加上這個數的「反碼」=> 10000 0110 + 1111 1001 = 0111 1111 結果是同樣的十進制127。
=> "原碼" + 「反碼」 = 127
=> "原碼" = 127 - 「反碼」
設 z 爲一字節二進制正數(就是須要咱們減去的數值),等式兩邊同時減去z
=>"原碼" - z = 127 - 「反碼」 - z
=>("原碼" - z) = 127 - (「反碼」 + z)
由於 "原碼" = 127 - 「反碼」
因此(「反碼」 + z) 爲("原碼" - z)的「新反碼」
反之("原碼" - z)爲(「反碼」 + z)的「新原碼」
這樣就把減法轉換爲了加法。
若是沒有看懂同窗沒關係,上圖=====>
4、」補碼「補漏洞
須要注意的是,當(「反碼」 + z)的值大於等於127時,計算結果就會出現錯誤
例如:十進制 8 = 二進制反碼 0000 1000
十進制 6 = 二進制反碼 0000 0110
十進制 -6 = 二進制原碼 1000 0110 = 二進制反碼 1111 1001
6 + (- 6) => (反碼)0000 0110 + (反碼)1111 1001 = (反碼)1111 1111 =>(原碼)1000 0000 結果爲-0,負0在數學上是無定義的
8 + (-6) => (反碼)0000 1000 + (反碼)1111 1001 = (反碼)0000 0001 =>(原碼)0000 0001結果爲1,明明8-6結果爲2啊,又出錯了
(-6) + (-6) => (反碼)1111 1001 + (反碼)1111 1001 = (反碼)1111 1010 =>(原碼)1000 0101結果爲-13,明明-6-6結果-12,又出錯了
錯誤是如何產生的了?
由於一個字節二進制的 0000 0000 有8個bit
這個二進制數字的排列數就有2^8個,結果就是256個排列數,256除以2等於128,就是有128種排列用於表示負數,128種排列來表示正數,
(如上圖)原反碼的圓盤只有127個指針數,跟一字節二進制自然的排列數128週期相比就少一個指針數
這樣就致使「原、反碼」算法的循環週期和二進制天然的循環週期不一致,不一樣步而產生錯誤。
引入」補碼「
求 "原碼" + 」補碼「?
由於」補碼「 = 」反碼「 + 1
=> "原碼" + 」補碼「 = "原碼" + 」反碼「 + 1
由於 "原碼" + 」反碼「 = 127
=> "原碼" + 」反碼「 + 1 = 127 + 1 = 128
=> "原碼" + 」補碼「 = 128
=> "原碼" = 128 - 」補碼「
設 z 爲一字節二進制數(就是須要咱們減去的數值),等式兩邊同時減去z
=> "原碼" - z = 128 - 」補碼「 - z
=> ("原碼" - z) = 128 - (」補碼「 + z)
同理(」補碼「 + z)是("原碼" - z)的新補碼。
這樣咱們就至關於把圓盤刻度增長一位值(見下圖)。
這樣算法週期和二進制排列數週期就相匹配了。
須要注意的是:
二進制128個排列是用來表示-128到-1的
二進制128個排列是用來表示0到127的
因此一字節二進制能表示帶符號數的範圍是-128到127.
規定-128 用 1000 0000(補碼) 表示,這個二進制很特殊它減1的效果和它取反的值同樣,因此它的原碼和補碼相等。
-1用 1111 1111(補碼)表示。