從原碼,反碼,補碼的設計理念來深入理解其原理


原碼,反碼,補碼大家都知道,下面通過解析為什么當初要這樣設計,讓你更透徹的理解它們的原理。

文章參考:

https://blog.csdn.net/afsvsv/article/details/94553228
https://blog.csdn.net/wu_nan_nan/article/details/54633506
https://www.zhihu.com/question/28685048
原文:https://blog.vchar.top/base/1611834985.html

計算方式

原碼:是計算機機器數中最簡單的一種形式,數值位就是真值的絕對值,即二進制表示的格式;其中最高位是符號位,符號位中0表示正數,1表示負數。

反碼:正數的反碼和原碼一樣;負數的反碼是它原碼除符號位外,其他位按位取反。

補碼:正數的補碼和原碼一樣;負數的補碼等於它反碼+1,或者說是等於它的原碼自低位向高位,尾數的第一個‘1’及其右邊的‘0’保持不變,左邊的各位按位取反,符號位不變。

為什么這樣要計算呢?

由於計算機的硬件設計決定,其本質都是以二進制碼來存儲和運算的;根據馮~諾依曼提出的經典計算機體系結構框架。一台計算機由運算器,控制器,存儲器,輸入和輸出設備組成。其中運算器,只有加法運算器,沒有減法運算器(據說一開始是有的,后來由於減法器硬件開銷太大,被廢了 )。

因此在計算機中是沒有減法的,只有加法運算。即減一個數相當於加上一個負數。

所以需要設計一種新的計算規則來實現計算機的加法運算;注意我們需要的是一個新的規則來適配計算機的加法運算,使其最終結果和我們現有的計算規則的結果一樣!!

下面我們以4位的二進制數為例做設計。

原碼

原碼:是最簡單的機器數表示法。用最高位表示符號位,‘1’表示負號,‘0’表示正號。其他位存放該數的二進制的絕對值。

下面這個以原碼的規則計算機中存儲的數據

/ 正數 / 負數
0 0000 -0 1000
1 0001 -1 1001
2 0010 -2 1010
3 0011 -3 1011
4 0100 -4 1100
5 0101 -5 1101
6 0110 -6 1110
7 0111 -7 1111

這種設計方式很簡單,雖然出現了-0和+0,但是還能接受;下面我們開始做運算:

	0001+0010=0011  ==>> 1+2=3      沒得問題
	0000+1000=1000  ==>> 0+(-0)=-0  可以接受
	0001+1001=1010  ==>> 1+(-1)=-2  哦,這個...

這種方式在正數之間進行沒得問題,但是有負數的運算就不行了,看來原碼干不了這個活啊;於是反碼來了。

反碼

我們知道,在十進制中一個數和其相反數相加等於0,對應的減法也可定義為一個數加上另一個數的相反數;基於這一點反碼的設計思路出來,那就是定義二進制的相反數求法。

直接按十進制的套用明顯不行,那么讓它的原碼除符號位外,按位取反;由於正數使用原碼進行計算沒得問題,就暫時不動它,只讓其適用於負數。得到如下的結果:

現在再來計算:

	0001+1110=1111  ==>> 1+(-1)=-0  現在正確了

注意現在計算機中實際存儲的是反碼了。

	1110+1101=1011  ==>> -1+(-2)=--4  哦,這個...

這種方式好像在計算負數+負數的時候不得行啊。

不過我們已經解決了相反數相加的問題了,對於負數我們直接讓其符號位固定為1即可達到正確結果。

0001+1110=1111 ==>> 1+(-1)=-0  這個看着怪別扭的

這種負0看着怪別扭的,同時需要在負數+負數的時候還要做個符號位強制位1的操作,太麻煩了,要想個辦法偷懶(平時編程中也應當有個偷懶的思維 hhh); 於是補碼出現了。

補碼

由於正數是沒得問題的,不做修改,所以正數的補碼等於他的原碼;負數的補碼等於反碼+1。(這只是一種算補碼的方式,多數書對於補碼就是這句話)

負數的補碼等於他的原碼自低位向高位,尾數的第一個‘1’及其右邊的‘0’保持不變,左邊的各位按位取反,符號位不變。

想想當年那些計算機學家(高級專業偷懶戶),並不會心血來潮的把反碼+1就定義為補碼。下面來看看其設計原因。

由於使用十進制的計算方式已經不能滿足二進制的需求了,因此我們需要跳出來,重新找靈感。

生活中的時鍾有12個刻度,如果時針現在在10點的位置,那么什么時候會停止在8點鍾的位置呢?

這個很簡單再過10個小時,或者2個小時前,那么得到如下公式:

10-2=8=10+10 時間超過12就會重新開始,這種稱為模。

在時鍾運算中,減去一個數,其實就相當於加上另外一個數(這個數與減數相加正好等於12,也稱為同余數)

通過時鍾的例子可以發現最終轉換后的計算表達式是2個正數相加,而從前面的結論中2個正數進行運算其符號位並不是必須的,那么現在設計補碼時我們暫時就將符號位去掉。

這也是為什么正數的符號位是0,負數的符號位是1的原因;因為如果正數的符號位是1的話,由於其符號位被忽略,當其參與運算時就會發生進位的情況,而使用0就不會有這種情況。

  • 同余數

現在就是需要求這個同余數的問題,根據數學中對同余數的定義:

    兩個整數a,b,若它們除以整數m所得的余數相等,則稱a,b對於模m同余。

例如,當m=12時,3跟15是同余的,因為3mod12=3=15mod12,對於同余,有如下結論:

    a,b是關於m同余的,當且僅當,二者相差m的整數倍,
    a−b=k×m, with k=……−2,−1,0,1,2,……
    即,
    a=b+k×m, with k=……−2,−1,0,1,2,……
    一個數x加a對m取余,等於x加a的同余b對m取余,即,
    (x+a) mod m=(x+b) mod m.
    由1.易知2.是成立的。

將參數帶入:

    3-15=-1*12  === 3=15+(-1)*12
    (x+3)%12=(x+15)%12

將上面的表達式在簡化下:

    若:a=b+m,則 (x+a) mod m = (x+b) mod m

那么現在該如何來求這個同余數呢?也就是找到負數補碼的求法。

若b為一個負數,表達式可以轉為類似如下:

a-b=m  ===>> 設c=-b,則:a+c=m

現在要 求a;通過上面的推導,我們在運算時可以減法轉換為加法,相當於將負數轉換為了正數,那么符合位也就沒有用了;因此我們新設計的補碼就可以不要符合位(因為都是正數的運算了)。

參考時鍾的案例,我們最終其實只關注二進制位數能夠表示的數(因為多余的位數也沒地方存儲),即時鍾刻度的最大值就是m,也就是二進制位數表達的最大值,例如4位的最大值就是16。

所以求a即計算m-c就是求其二進制的另一半。而c的另一半就是把二進制位上的0變1、1變0即取反,它們相加后全是1,而m是其最大值+1,因此還需要加1才行。而這就是補碼的求法。這個計算的方法還可以參考時鍾的情況。下面來實際計算驗證一下:

示例1:

    3-5=-2=(3+11)%16
    
    0011(3的補碼) + 1011(-5的補碼,11的原碼) = 1110
    1110是一個補碼(此時最高位就是符號位)轉為原碼:1010 即為-2

示例2:

    5-3=2=(5+13)%16
    
    0101(5的補碼) + 1101(-3的補碼,13的原碼) = 1 0010
    由於總共只有4位,產生的進位會被丟掉,因此最終的補碼為 0010 轉為原碼:0010即為2

示例3:

0001+1111=1 0000 ==>> 1+(-1)=0  這樣之前的-0問題也解決了

最終使用補碼滿足了我們的要求,因此在計算機中實際存儲的是補碼,進行操作的時候也是通過補碼來進行操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM