背景問(wèn)題:你知道計(jì)算機(jī)中以什么形式存儲(chǔ)整數(shù)嗎?是符號(hào)位加值位嗎?值位是按照正常的二進(jìn)制方式存儲(chǔ)的嗎?假如用3位二進(jìn)制進(jìn)行存儲(chǔ),符號(hào)位0正1負(fù),1是存成001,-1是存成101嗎?

答:使用補(bǔ)碼的方式而不是正常的方式存儲(chǔ),雖然是符號(hào)位加值位,但符號(hào)位承載的信息和值位的值不是你想象中的方式,比如用3位二進(jìn)制進(jìn)行存儲(chǔ),符號(hào)位0正1負(fù),1會(huì)存成001,-1會(huì)存成111 

首先來(lái)回憶一下剛學(xué)計(jì)算機(jī)時(shí)候的教材里是怎么解釋補(bǔ)碼的:

- 原碼表示法是機(jī)器數(shù)的一種簡(jiǎn)單的表示法。其符號(hào)位用0表示正號(hào),用1表示負(fù)號(hào),數(shù)值一般用二進(jìn)制形式表示。
- 機(jī)器數(shù)的反碼可由原碼得到。如果機(jī)器數(shù)是正數(shù),則該機(jī)器數(shù)的反碼與原碼一樣;如果機(jī)器數(shù)是負(fù)數(shù),則該機(jī)器數(shù)的反碼是對(duì)它的原碼(符號(hào)位除外)各位取反而得到的。
- 機(jī)器數(shù)的補(bǔ)碼可由原碼得到。如果機(jī)器數(shù)是正數(shù),則該機(jī)器數(shù)的補(bǔ)碼與原碼一樣;如果機(jī)器數(shù)是負(fù)數(shù),則該機(jī)器數(shù)的補(bǔ)碼是它的反碼在未位加1而得到的。
- 現(xiàn)代計(jì)算機(jī)中普遍使用補(bǔ)碼表示法,而不是原碼。

 WTF!WHY?!

OK,下面我們來(lái)一一解答

 

一、計(jì)算機(jī)為什么使用補(bǔ)碼的形式存儲(chǔ)整數(shù)

  出于簡(jiǎn)化計(jì)算機(jī)基本電路的考慮,讓加減法都只需要用加法電路實(shí)現(xiàn)。所以需要把減去一個(gè)正數(shù)或加上一個(gè)負(fù)數(shù)都用加上一個(gè)正數(shù)的方式來(lái)表示,于是在存儲(chǔ)的時(shí)候,負(fù)數(shù)被直接存儲(chǔ)成一種可以直接當(dāng)成正數(shù)來(lái)相加的形式 (正數(shù)不變,所以以后的討論中有時(shí)候略去正數(shù)),這種形式就是補(bǔ)碼

二、那么補(bǔ)碼具體是什么?補(bǔ)碼是怎么做到加一個(gè)數(shù)跟減另一個(gè)數(shù)一樣效果的?

1. 先從時(shí)鐘這個(gè)身邊的例子理解 “加一個(gè)數(shù)跟減另一個(gè)數(shù)一樣效果”
  假設(shè)你對(duì)鐘的時(shí)候如果發(fā)現(xiàn)它是6點(diǎn),但實(shí)際上現(xiàn)在是2點(diǎn),也就是它走快了4個(gè)小時(shí),你可以有兩種做法進(jìn)行校正,一種是逆時(shí)針撥回4個(gè)小時(shí)到2點(diǎn),另一種是順時(shí)針撥6個(gè)小時(shí)到12點(diǎn)然后再撥2小時(shí),也就是順時(shí)針撥8個(gè)小時(shí)。也就是對(duì)于時(shí)鐘的表盤(pán)來(lái)說(shuō),-4 = +8,同樣還會(huì)有 -1 = +11,-5 = +7,甚至還有 -4 = +8 = +20 = +32 = -16
  他們間隱藏了什么規(guī)律呢?在數(shù)學(xué)中,-4、+8、+20、+32、-16可以歸為符合某個(gè)條件的同一類(lèi)數(shù)字——對(duì)于模12同余。wiki上對(duì)于模的定義是 “兩個(gè)整數(shù)a、b,若它們除以正整數(shù)m所得的余數(shù)相等,則稱(chēng)a、b對(duì)于模m同余”
  而在一個(gè)可溢出計(jì)數(shù)系統(tǒng)中,把計(jì)數(shù)系統(tǒng)容量作為模,那么所有對(duì)此模同余的數(shù)在此計(jì)數(shù)系統(tǒng)中都會(huì)有同樣的表示(加這個(gè)數(shù)也一樣)。比如時(shí)鐘表盤(pán)就是一個(gè)可溢出計(jì)數(shù)系統(tǒng),模為12;一個(gè)n位二進(jìn)制構(gòu)成的計(jì)數(shù)系統(tǒng)中,因?yàn)闀?huì)舍棄溢出的高位,所以也是一個(gè)可溢出的計(jì)數(shù)系統(tǒng),模為2^n(從0數(shù)到2^n-1)
  所以假設(shè)一個(gè)3位二進(jìn)制構(gòu)成的模為8的計(jì)數(shù)系統(tǒng),-2,-10,6,14都表示同樣的數(shù),也就是減10和加14是一樣的效果

2. 引出“補(bǔ)碼”
  為了讓“補(bǔ)碼”實(shí)現(xiàn) “加一個(gè)正數(shù)跟加一個(gè)負(fù)數(shù)(減一個(gè)正數(shù)) 一樣的效果”,“補(bǔ)碼”就可以是跟原負(fù)數(shù)對(duì)于模同余的正數(shù)
  在計(jì)算機(jī)中為了減少不必要的運(yùn)算,負(fù)數(shù)的“補(bǔ)碼”就取其中最小的正數(shù),正數(shù)直接就是它自己(而且如果負(fù)得太多,補(bǔ)一個(gè)模都不是正數(shù),那其實(shí)算是左邊越界溢出)
  可能是通過(guò)原碼求“補(bǔ)碼”就是一個(gè)補(bǔ)模運(yùn)算,所以把它叫做“補(bǔ)碼”
 ?。ǖ⒁?,這里的“補(bǔ)碼”都被我打上了雙引號(hào),因?yàn)檫@還不是計(jì)算機(jī)里真正的存儲(chǔ)的補(bǔ)碼形式,它應(yīng)該叫補(bǔ)數(shù),不過(guò)相信我,已經(jīng)差不多了)

三、但這種“補(bǔ)碼”表示還有問(wèn)題

  通過(guò)轉(zhuǎn)換成“補(bǔ)碼”,減一個(gè)數(shù)確實(shí)變成加一個(gè)數(shù)了,看似很不錯(cuò),但卻有一個(gè)明顯的問(wèn)題,那就是數(shù)本身的符號(hào)丟失了
  我們只存儲(chǔ)了一個(gè)在加法運(yùn)算中方便運(yùn)算的負(fù)數(shù)(甚至在結(jié)果為負(fù)數(shù)時(shí)計(jì)算結(jié)果都會(huì)不正確),但它卻不是一個(gè)能正常表示自己的負(fù)數(shù)(無(wú)法逆運(yùn)算回去)
  比如3位二進(jìn)制,正常能表示0~7,使用補(bǔ)碼法能進(jìn)一步表示 -8~-1的運(yùn)算,但不能真正表示 -8~-1

四、怎么完美解決“補(bǔ)碼”的正負(fù)表示問(wèn)題?

  不知道大牛是怎么想到的,但最后我們看到的這種做法,是真的Magic。只需要在左邊加一個(gè)二進(jìn)制位來(lái)表示正負(fù),就能同時(shí)實(shí)現(xiàn)這幾個(gè)效果:

  1. 在保持補(bǔ)碼特性的前提下。也就是減一個(gè)數(shù)還是照樣變成加一個(gè)數(shù)

  2. 增加正負(fù)的表示。能真正表示 -8~-1了,就只用看符號(hào)位是0還是1

  3. 還能讓運(yùn)算時(shí)不用另外區(qū)分符號(hào)位,直接把符號(hào)位當(dāng)成值位進(jìn)行運(yùn)算,而結(jié)果的正負(fù)號(hào)自然會(huì)符合這個(gè)正負(fù)表示法(也就是符號(hào)位的進(jìn)位和值位的進(jìn)位都會(huì)自然地合理)(有一種理解方式是,把這個(gè)負(fù)號(hào)1當(dāng)成減一個(gè)模)

  具體來(lái)說(shuō)是在左邊加一個(gè)符號(hào)位,這個(gè)符號(hào)位不參與“補(bǔ)碼”的運(yùn)算,始終表示數(shù)的正負(fù),但在加法運(yùn)算中跟值位一樣參與運(yùn)算。加了一個(gè)這樣的符號(hào)位的“補(bǔ)碼”就是真正計(jì)算機(jī)中存儲(chǔ)的補(bǔ)碼了

五、最后總結(jié)正常人怎么求補(bǔ)碼

  對(duì)負(fù)數(shù)求最小正同余數(shù)(模為二進(jìn)制值位存儲(chǔ)容量),把它們放入值位,符號(hào)位置1

  到這里“負(fù)整數(shù)為什么存成補(bǔ)碼?”這個(gè)問(wèn)題基本就解答清楚了,你會(huì)發(fā)現(xiàn)里邊都沒(méi)有反碼的影子,對(duì),就是這樣,用反碼以及那套教材里的計(jì)算補(bǔ)碼的方法來(lái)理解負(fù)整數(shù)的表示都是緣木求魚(yú),原理上根本不需要它們,那它們是用來(lái)干什么的呢?值位取反加一這種算法是怎么冒出來(lái)的?接著往下看補(bǔ)充內(nèi)容你就知道了

補(bǔ)充計(jì)算機(jī)中求補(bǔ)碼的簡(jiǎn)便算法

  對(duì)于計(jì)算機(jī)來(lái)說(shuō),上面這個(gè)過(guò)程有點(diǎn)繁瑣,尤其是需要先求模,然后求最小正同余數(shù),而且這個(gè)過(guò)程是非?;A(chǔ)的過(guò)程,一點(diǎn)點(diǎn)優(yōu)化都能有很大的性能提升
  然后這個(gè)過(guò)程就被大牛優(yōu)化成這樣:先直接把負(fù)數(shù)的絕對(duì)值存到值位,符號(hào)位為負(fù),然后對(duì)值位取反+1,就得到了補(bǔ)碼(這也是很多教材里告訴我們補(bǔ)碼怎么求的方法。。。用這個(gè)理解補(bǔ)碼,真是夠了)
  能看到其實(shí)優(yōu)化的是求“補(bǔ)碼”:值位取反加一 = 最小正同余數(shù),下面可以證明一下:

  1. 用3位二進(jìn)制值位[abc]表示一個(gè)不會(huì)造成溢出的負(fù)數(shù)F:F = -( a*2^2 + b*2^1 + c)(a,b,c ∈ {0,1})

  2. 對(duì)F的值位取反 :F(反) = (1-a)*2^2 + (1-b)*2^1 + 1-c = 2^2 + 2^1 +1 - ( a*2^2 + b*2^1 + c ) = 2^3 -1 + F

  3. 然后3位二進(jìn)制的模等于2^3,所以F的補(bǔ)碼 F(補(bǔ)) = F + 2^3

  4. 所以結(jié)果就出來(lái)了: F(反) = 2^3 -1 + F = F(補(bǔ)) -1 得到 F(補(bǔ)) = F(反)+1

可能是目前為止最好的理解補(bǔ)碼的文章XD,還請(qǐng)博友批評(píng)指正~

http://www.cnblogs.com/bellkosmos/p/7150105.html