原碼反碼補(bǔ)碼
/*最近在重新學(xué)習(xí)一遍C語言,以更加深入的理解C語言,為C++打下比較堅(jiān)實(shí)的基礎(chǔ)。此次學(xué)習(xí)主要依據(jù)的書籍依然為《c primer plus》中文版 第五版。至此,我創(chuàng)建了C/C++這一個(gè)分類,用以記錄學(xué)習(xí)C/C++的過程,不斷提高自己。*/
在前面的兩章的學(xué)習(xí)中,并未遇到很大的問題。其中的一個(gè)就是關(guān)于變量,變量名,聲明,定義,初始化等的相關(guān)基礎(chǔ)知識(shí),這點(diǎn)會(huì)在下一篇的文章中進(jìn)行講解。目前遇到的問題是有關(guān)數(shù)據(jù)類型,數(shù)據(jù)表示,計(jì)算機(jī)存儲(chǔ)的問題。這里涉及到有關(guān)原碼,反碼,補(bǔ)碼的基礎(chǔ)知識(shí),在網(wǎng)上查閱資料,發(fā)現(xiàn)一篇講的很不錯(cuò)的文章?!疚恼麻_頭給出鏈接】:
?? https://www.imooc.com/article/16813?block_id=tuijian_wz????????????????? //原作者保留權(quán)利
以下是文章內(nèi)容:
本文從原碼講起。通過簡(jiǎn)述原碼,反碼和補(bǔ)碼存在的作用,加深對(duì)補(bǔ)碼的認(rèn)識(shí)。力爭(zhēng)讓你對(duì)補(bǔ)碼的概念不再局限于:負(fù)數(shù)的補(bǔ)碼等于反碼加一。
接觸過計(jì)算機(jī)或電子信息相關(guān)課程的同學(xué),應(yīng)該都或多或少看過補(bǔ)碼這哥仨。每次都是在課本的最前幾頁,來上這么一段:什么反碼是原碼除符號(hào)位,按位取反。補(bǔ)碼等于反碼加一。然后給整得莫名其妙,稀里糊涂地,接著就是翻頁,反正后面的內(nèi)容也跟三碼沒多大關(guān)系。
我原來也是看了好幾遍都沒看懂。古人云:事不過三。學(xué)C語言的時(shí)候,看過一次。不懂?看《計(jì)算機(jī)基本組成原理》的時(shí)候看過,還是不懂!到了大三,上《單片微機(jī)原理與接口技術(shù)》的時(shí)候仍舊是不懂。到了期末,復(fù)習(xí)的時(shí)候,和宿舍的人瞎聊。說講講這些碼呀,我說我也不是很清楚呀。然后就一邊說怎么求碼,一邊算。玩著玩著,突然就明白了。我說好,打住。不說了,放假我在好好整理下思路,于是就有了這篇額。。算討論帖吧。
好了,廢話不多說。開始我們的原碼,反碼,補(bǔ)碼之旅。
(一)預(yù)備知識(shí)
認(rèn)識(shí)二進(jìn)制,十六進(jìn)制。會(huì)二進(jìn)制與十進(jìn)制的相互轉(zhuǎn)化運(yùn)算
由計(jì)算機(jī)的硬件決定,任何存儲(chǔ)于計(jì)算機(jī)中的數(shù)據(jù),其本質(zhì)都是以二進(jìn)制碼存儲(chǔ)。
根據(jù)馮~諾依曼提出的經(jīng)典計(jì)算機(jī)體系結(jié)構(gòu)框架。一臺(tái)計(jì)算機(jī)由運(yùn)算器,控制器,存儲(chǔ)器,輸入和輸出設(shè)備組成。其中運(yùn)算器,只有加法運(yùn)算器,沒有減法運(yùn)算器(據(jù)說一開始是有的,后來由于減法器硬件開銷太大,被廢了 )
所以,計(jì)算機(jī)中的沒法直接做減法的,它的減法是通過加法來實(shí)現(xiàn)的。你也許會(huì)說,現(xiàn)實(shí)世界中所有的減法也可以當(dāng)成加法的,減去一個(gè)數(shù),可以看作加上這個(gè)數(shù)的相反數(shù)。當(dāng)然沒錯(cuò),但是前提是要先有負(fù)數(shù)的概念。這就為什么不得不引入一個(gè)該死的符號(hào)位。
而且從硬件的角度上看,只有正數(shù)加負(fù)數(shù)才算減法。
正數(shù)與正數(shù)相加,負(fù)數(shù)與負(fù)數(shù)相加,其實(shí)都可以通過加法器直接相加。
原碼,反碼,補(bǔ)碼的產(chǎn)生過程,就是為了解決,計(jì)算機(jī)做減法和引入符號(hào)位(正號(hào)和負(fù)號(hào))的問題。
本文可能比較長(zhǎng),沒必要一下子讀完。原碼,反碼,補(bǔ)碼,按章讀。
重點(diǎn)在于講補(bǔ)碼,到了補(bǔ)碼可能有些繞,建議帶著筆,寫出二進(jìn)制數(shù)一起算。
表達(dá)可能不夠清楚嚴(yán)謹(jǐn),望見諒。
(二)原碼
原碼:是最簡(jiǎn)單的機(jī)器數(shù)表示法。用最高位表示符號(hào)位,‘1’表示負(fù)號(hào),‘0’表示正號(hào)。其他位存放該數(shù)的二進(jìn)制的絕對(duì)值。
若以帶符號(hào)位的四位二進(jìn)值數(shù)為例
1010? : 最高位為‘1’,表示這是一個(gè)負(fù)數(shù),其他三位為‘010’,
? ? ? 即(0*2^2)+(1*2^1)+(0*2^0)=2(‘^’表示冪運(yùn)算符)
? ? ? 所以1010表示十進(jìn)制數(shù)(-2)。
下圖給出部份正負(fù)數(shù)數(shù)的二進(jìn)制原碼表示法

OK,原碼表示法很簡(jiǎn)單有沒有,雖然出現(xiàn)了+0和-0,但是直觀易懂。
于是,我們高興的開始運(yùn)算。
0001+0010=0011? ? (1+2=3)OK
0000+1000=1000? ? (+0+(-0)=-0) 額,問題不大
0001+1001=1010? ? (1+(-1)=-2)
噢,1+(-1)=-2,這仿佛是在逗我呢。
于是我們可以看到其實(shí)正數(shù)之間的加法通常是不會(huì)出錯(cuò)的,因?yàn)樗褪且粋€(gè)很簡(jiǎn)單的二進(jìn)制加法。
而正數(shù)與負(fù)數(shù)相加,或負(fù)數(shù)與負(fù)數(shù)相加,就要引起莫名其妙的結(jié)果,這都是該死的符號(hào)位引起的。0分為+0和-0也是因他而起。
所以原碼,雖然直觀易懂,易于正值轉(zhuǎn)換。但用來實(shí)現(xiàn)加減法的話,運(yùn)算規(guī)則總歸是太復(fù)雜。于是反碼來了。
(三)反碼
我們知道,原碼最大的問題就在于一個(gè)數(shù)加上他的相反數(shù)不等于零。
例如:0001+1001=1010 (1+(-1)=-2)?0010+1010=1100 (2+(-2)=-4)
于是反碼的設(shè)計(jì)思想就是沖著解決這一點(diǎn),既然一個(gè)負(fù)數(shù)是一個(gè)正數(shù)的相反數(shù),那我們干脆用一個(gè)正數(shù)按位取反來表示負(fù)數(shù)試試。
反碼:正數(shù)的反碼還是等于原碼
負(fù)數(shù)的反碼就是他的原碼除符號(hào)位外,按位取反。
若以帶符號(hào)位的四位二進(jìn)制數(shù)為例:
3是正數(shù),反碼與原碼相同,則可以表示為0011
-3的原碼是1011,符號(hào)位保持不變,低三位(011)按位取反得(100)
所以-3的反碼為1100
下圖給出部分正負(fù)數(shù)的二進(jìn)制數(shù)反碼表示法

對(duì)著上圖,我們?cè)僭囍梅创a的方式解決一下原碼的問題
0001+1110=1111 (1+(-1)= - 0)
互為相反數(shù)相加等于0,解決。雖然是得到的結(jié)果是1111也就是-0
好,我們?cè)僭囍鲆幌聝蓚€(gè)負(fù)數(shù)相加
1110(-1)+1101(-2)=1011(-4)
噢,好像又出現(xiàn)了新問題
(-1)+(-2)=(-4)?
不過好像問題不大,因?yàn)?011(是-4的反碼,但是從原碼來看,他其實(shí)是-3。巧合嗎?)
我們?cè)倏磦€(gè)例子吧
1110(-1)+1100(-3)=1010(-5)
確實(shí)是巧合,看來相反數(shù)問題是解決了,但是卻讓兩個(gè)負(fù)數(shù)相加的出錯(cuò)了。
但是實(shí)際上,兩個(gè)負(fù)數(shù)相加出錯(cuò)其實(shí)問題不大。我們回頭想想我們的目的是什么?是解決做減法的問題,把減法當(dāng)成加法來算。
兩個(gè)正數(shù)相加和兩個(gè)負(fù)數(shù)相加,其實(shí)都是一個(gè)加法問題,只是有無符號(hào)位罷了。而正數(shù)+負(fù)數(shù)才是真正的減法問題。
也就是說只要正數(shù)+負(fù)數(shù)不會(huì)出錯(cuò),那么就沒問題了。負(fù)數(shù)加負(fù)數(shù)出錯(cuò)沒關(guān)系的,負(fù)數(shù)的本質(zhì)就是正數(shù)加上一個(gè)符號(hào)位而已。
在原碼表示法中兩個(gè)負(fù)數(shù)相加,其實(shí)在不溢出的情況下結(jié)果就只有符號(hào)位出錯(cuò)而已(1001+1010=0011)
反碼的負(fù)數(shù)相加出錯(cuò),其實(shí)問題不大。我們只需要加實(shí)現(xiàn)兩個(gè)負(fù)數(shù)加法時(shí),將兩個(gè)負(fù)數(shù)反碼包括符號(hào)位全部按位取反相加,然后再給他的符號(hào)位強(qiáng)行置‘1’就可以了。
所以反碼表示法其實(shí)已經(jīng)解決了減法的問題,他不僅不會(huì)像原碼那樣出現(xiàn)兩個(gè)相反數(shù)相加不為零的情況,而且對(duì)于任意的一個(gè)正數(shù)加負(fù)數(shù),如:
0001(1)+1101(-2)=1110(-1)?計(jì)算結(jié)果是正確的。所以反碼與原碼比較,最大的優(yōu)點(diǎn),就在于解決了減法的問題。
但是我們還是不滿足為什么?0001+1110=1111 (1+(-1)=-0)?為什么是-0呢
而且雖然說兩個(gè)負(fù)數(shù)相加問題不大,但是問題不大,也是問題呀。好吧,處女座。接下來就介紹我們的大boss補(bǔ)碼。
(四)補(bǔ)碼
補(bǔ)碼:正數(shù)的補(bǔ)碼等于他的原碼
負(fù)數(shù)的補(bǔ)碼等于反碼+1。
(這只是一種算補(bǔ)碼的方式,多數(shù)書對(duì)于補(bǔ)碼就是這句話)
在《計(jì)算機(jī)組成原理中》,補(bǔ)碼的另外一種算法?是
負(fù)數(shù)的補(bǔ)碼等于他的原碼自低位向高位,尾數(shù)的第一個(gè)‘1’及其右邊的‘0’保持不變,左邊的各位按位取反,符號(hào)位不變。
OK,補(bǔ)碼就講完了。再見??!
還是莫名其妙有沒有,為什么補(bǔ)碼等于反碼加1,為什么自低位向高位取反...................?
其實(shí)上面那兩段話,都只是補(bǔ)碼的求法,而不是補(bǔ)碼的定義。很多人以為求補(bǔ)碼就要先求反碼,其實(shí)并不是。
那些雞賊的計(jì)算機(jī)學(xué)家,并不會(huì)心血來潮的把反碼+1就定義為補(bǔ)碼。只不過是補(bǔ)碼正好就等于反碼加1罷了。
所以,忘記那些書上那句負(fù)數(shù)的補(bǔ)碼等于它的反碼+1。就這句話把我們帶入了理解的誤區(qū)。
這就是后來我明白為什么我看的那本《計(jì)算機(jī)組成原理》,要特意先講補(bǔ)碼,再講反碼。
然后說負(fù)數(shù)的補(bǔ)碼等于他的原碼自低位向高位,尾數(shù)的第一個(gè)‘1’及其右邊的‘0’保持不變,左邊的各位按位取反,符號(hào)位不變。
但是上面這句話,同樣不是補(bǔ)碼的定義,它只是補(bǔ)碼的另外一種求法。它的存在,告訴我們忘記那句該死的‘反碼+1’它并不是必須的。
如果你有興趣了解,補(bǔ)碼的嚴(yán)格說法,我建議你可以看一下《計(jì)算機(jī)組成原理》。它會(huì)用‘模’和‘同余’的概念,嚴(yán)謹(jǐn)?shù)亟忉屟a(bǔ)碼。
接下來我只想聊聊補(bǔ)碼的思想。
(五)補(bǔ)碼的思想
補(bǔ)碼的思想,第一次見可能會(huì)覺得很繞,但是如果你肯停下來仔細(xì)想想,絕對(duì)會(huì)覺得非常美妙。
補(bǔ)碼的思想其實(shí)就來自于生活,只是我們沒注意到而已。時(shí)鐘,經(jīng)緯度,《易經(jīng)》里的八卦。
補(bǔ)碼的思想其實(shí)就類似于生活中的時(shí)鐘
好吧,我其實(shí)不想用類似,好像這種詞,因?yàn)轭惐鹊?,終究不是事物本身。而且不嚴(yán)謹(jǐn)會(huì)讓我懷疑我不是工科僧,說得好像我嚴(yán)謹(jǐn)過似的,哈哈
如果說現(xiàn)在時(shí)針現(xiàn)在停在10點(diǎn)鐘,那么什么時(shí)候時(shí)針會(huì)停在八點(diǎn)鐘呢?
簡(jiǎn)單,過去隔兩個(gè)小時(shí)的時(shí)候,是八點(diǎn)鐘。未來過十個(gè)小時(shí)的時(shí)候也是八點(diǎn)鐘
也就是說時(shí)間正撥10小時(shí),或是倒撥2小時(shí)都是八點(diǎn)鐘。
也就是10-2=8,而且 10+10=8(10+10=10+2+8=12+8=8)
這個(gè)時(shí)候滿12說明時(shí)針在走第二圈了,又走了8小時(shí),所以時(shí)針正好又停在八點(diǎn)鐘。
所以12在時(shí)鐘運(yùn)算中,稱之為模,超過了12就會(huì)重新從1開始算了。
也就是說, 10-2和10+10從另一個(gè)角度來看是等效的,它都使時(shí)針指向了八點(diǎn)鐘。
既然是等效的,那在時(shí)鐘運(yùn)算中,減去一個(gè)數(shù),其實(shí)就相當(dāng)于加上另外一個(gè)數(shù)(這個(gè)數(shù)與減數(shù)相加正好等于12,也稱為同余數(shù))
這就是補(bǔ)碼所謂模運(yùn)算思想的生活例子
在這里,我們?cè)俅螐?qiáng)調(diào)原碼,反碼,補(bǔ)碼的引入是為了解決做減法的問題。在原碼,反碼表示法中,我們把減法化為加法的思維是減去一個(gè)數(shù),等于加上一個(gè)數(shù)的相反數(shù),結(jié)果發(fā)現(xiàn)引入了符號(hào)位,卻因?yàn)榉?hào)位造成了各種意向不到的問題。
但是從上面的例子中,我們可以看到其實(shí)減去一個(gè)數(shù),對(duì)于數(shù)值有限制,有溢出的運(yùn)算(模運(yùn)算)來說,其實(shí)也相當(dāng)于加上這個(gè)數(shù)的同余數(shù)。
也就是說,我們不引入負(fù)數(shù)的概念,就可以把減法當(dāng)成加法來算。所以接下來我們聊4位二進(jìn)制數(shù)的運(yùn)算,也不必急于引入符號(hào)位。因?yàn)檠a(bǔ)碼的思想,把減法當(dāng)成加法時(shí)并不是必須要引入符號(hào)位的。
而且我們可以通過下面的例子,也許能回答另一個(gè)問題,為什么負(fù)數(shù)的符號(hào)位是‘1’,而不是正數(shù)的符號(hào)位是‘1’。
(六)補(bǔ)碼實(shí)例
好吧,接下來我們就做一做四位二進(jìn)制數(shù)的減法吧(先不引入符號(hào)位)
0110(6)-0010(2)【6-2=4,但是由于計(jì)算機(jī)中沒有減法器,我們沒法算】
這個(gè)時(shí)候,我們想想時(shí)鐘運(yùn)算中,減去一個(gè)數(shù),是可以等同于加上另外一個(gè)正數(shù)(同余數(shù))
那么這個(gè)數(shù)是什么呢?從時(shí)鐘運(yùn)算中我們可以看出這個(gè)數(shù)與減數(shù)相加正好等于模。
那么四位二進(jìn)制數(shù)的模是多少呢?也就是說四位二進(jìn)制數(shù)最大容量是多少?其實(shí)就是2^4=16=10000B
那么2的同余數(shù),就等于10000-0010=1110(14)
既然如此
0110(6)-0010(2)=0110(6)+1110(14)=10100(20=16+4)
OK,我們看到按照這種算法得出的結(jié)果是10100,但是對(duì)于四位二進(jìn)制數(shù),最大只能存放4位(硬件決定了),如果我們低四位,正好是0100(4),正好是我們想要的結(jié)果,至于最高位的‘1’,計(jì)算機(jī)會(huì)把他放入psw寄存器進(jìn)位位中。8位機(jī)則會(huì)放在cy中,x86會(huì)放在cf中(這個(gè)我們不作討論)
這個(gè)時(shí)候,我們?cè)傧胂朐谒奈欢M(jìn)制數(shù)中,減去2,就相當(dāng)于加上它的同余數(shù)14(至于它們?yōu)槭裁赐?,還是建議看《計(jì)算機(jī)組成原理》)
但是減去2,從另外一個(gè)角度來說,也是加上(-2)。即加上(-2)和加上14其實(shí)得到的二進(jìn)制結(jié)果除了進(jìn)位位,結(jié)果是一樣的。
如果我們把1110(14)的最高位看作符號(hào)位后就是(-2)的補(bǔ)碼,這可能也是為什么負(fù)數(shù)的符號(hào)位是‘1’而不是‘0’,
而且在有符號(hào)位的四位二進(jìn)制數(shù)中,能表示的只有‘-8~7’,而無符號(hào)位數(shù)(14)的作用和有符號(hào)數(shù)(-2)的作用效果其實(shí)是一樣的。
那正數(shù)的補(bǔ)碼呢?加上一個(gè)正數(shù),加法器就直接可以實(shí)現(xiàn)。所以它的補(bǔ)碼就還是它本身。
下圖給出帶符號(hào)位四位二進(jìn)制的補(bǔ)碼表示法

到這里,我們發(fā)現(xiàn)原碼,反碼的問題,補(bǔ)碼基本解決了。
在補(bǔ)碼中也不存在負(fù)零了,因?yàn)?000表示-8
這是因?yàn)楦鶕?jù)上面的補(bǔ)碼圖,做減法時(shí),0001(1)+1111(-1)=0000
我們?cè)僖膊恍枰粋€(gè)1000來表示負(fù)0了,就把它規(guī)定為-8
負(fù)數(shù)與負(fù)數(shù)相加的問題也解決了1111(-1)+1110(-2)=1101(-3)
可能說得有點(diǎn)繞,但是實(shí)在是沒辦法。其實(shí)我覺得補(bǔ)碼還可以這樣畫。

很優(yōu)美有沒有,如果你想想地理課本,0不就相當(dāng)于本初子午線,-8不就是180°,而正數(shù)相當(dāng)于西經(jīng),負(fù)數(shù)相當(dāng)于東經(jīng)。
(七)為何這樣求補(bǔ)碼
然后我們?cè)賮砜纯礊槭裁簇?fù)數(shù)的補(bǔ)碼的求法為什么是反碼+1
因?yàn)樨?fù)數(shù)的反碼加上這個(gè)負(fù)數(shù)的絕對(duì)值正好等于1111,再加1,就是1000,也就是四位二進(jìn)數(shù)的模
而負(fù)數(shù)的補(bǔ)碼是它的絕對(duì)值的同余數(shù),可以通過模減去負(fù)數(shù)的絕對(duì)值,得到他的補(bǔ)碼。
所以 負(fù)數(shù)的補(bǔ)碼就是它的補(bǔ)碼+1。
有點(diǎn)繞吧,只能說很難算清楚,你們還是自己算算吧。還有上面我提到的另外一種算法。
接下來,我要說一下我自己算補(bǔ)碼的小技巧。
看上面那個(gè)圖。
如果我們把-8當(dāng)成負(fù)數(shù)的原點(diǎn)。那么-5的補(bǔ)碼是多少呢?
-5=-8+3
-5的補(bǔ)碼就是-8的補(bǔ)碼加3
1000(-8) +0011(3)=1011(-5)
所以完全可以口算出-5的補(bǔ)碼是1011
當(dāng)然,也可以記住-1的補(bǔ)碼是1111口算減法得出
對(duì)于八位加法器的話,可以把-128當(dāng)補(bǔ)碼原點(diǎn)。十六位可以把-32768當(dāng)補(bǔ)碼原點(diǎn)。
是的,128是256(八位二進(jìn)制數(shù)的模)的一半,32768是65536(十六位二進(jìn)數(shù)的模)的一半
也很方便有沒有,而且簡(jiǎn)單的是
補(bǔ)碼原點(diǎn)總是最高位是‘1’,其他位是‘0’
所以做加法總是簡(jiǎn)單得可以口算。
OK,原碼,反碼,補(bǔ)碼之旅就到這里結(jié)束。補(bǔ)碼第一次看總會(huì)覺得很繞,想言簡(jiǎn)意賅,就怕哪里遺漏了。講得細(xì)致,又不免連自己都覺得啰里啰嗦。謝觀!
在這篇文章和網(wǎng)上其他學(xué)習(xí)資料的參考中,較好的明白原碼,反碼,補(bǔ)碼的原理與應(yīng)用。應(yīng)該注意的是,《計(jì)算機(jī)組成原理》這本書籍里面有更加權(quán)威的講解,但可能比較難懂。
下一篇文章將會(huì)講解:關(guān)于變量,變量名,聲明,定義,初始化,賦值以及編譯器等的相關(guān)基礎(chǔ)知識(shí)。
相關(guān)閱讀
最近花了點(diǎn)時(shí)間對(duì)計(jì)算機(jī)的原碼,反碼和補(bǔ)碼進(jìn)行了研究,對(duì)為什么要有反碼和補(bǔ)碼以及他們這么設(shè)計(jì)的原因有了一定的理解 機(jī)器數(shù) 一個(gè)
整數(shù)補(bǔ)碼——如何定義相反數(shù)。 出現(xiàn)的原因 想要表示3+5,可以轉(zhuǎn)化為8位2進(jìn)制:0000 0011+0000 0101=0000 1001。結(jié)合二進(jìn)制加法,這是
這之前,遇到什么原碼,反碼,補(bǔ)碼,就頭疼,其實(shí)遇到一個(gè)自己怕的問題,就一定要解決它,吃掉它,這樣心里的心結(jié)就解決了,不然等到以后,每次遇到都
原碼(1) 原碼:在數(shù)值前直接加一符號(hào)位的表示法。例如: 符號(hào)位 數(shù)值位byte的取值范圍[+7]原= 0 0000111 B[-7]原= 1 0000111 B注意:byte
重點(diǎn)關(guān)注紅色字體部分。 1. 為何補(bǔ)碼數(shù)值為原碼數(shù)值取反+1? 兩次加模, 如果數(shù)值位為n, 那么模為 2的n次方,兩次加模為 2 的 n+1 次