前兩天看到書里的小實例,涉及到移位運算符,還和同事研究了半天,現(xiàn)在深入總結(jié)一下。
注:大部分內(nèi)容來自網(wǎng)絡(luò)
原碼
最高位為符號位,“0”表示正,“1”表示負,其余位表示數(shù)值的大小
反碼
正數(shù)的反碼與其原碼相同;負數(shù)的反碼是對其原碼按位取反,但符號位除外。
補碼
正數(shù)的補碼與其原碼相同;負數(shù)的補碼是在其反碼的末位加1。
吐槽,這里的計算方式都很特別啊。
問:負數(shù)在計算機中如何表示?
計算機內(nèi)部采用2的補碼(Two's Complement)表示負數(shù)。(即平常大家都叫補碼)
問:在計算機中怎么計算減法?
全部換成補碼形式相加計算。
不斷的提問,找資料,解決問題……后來,我發(fā)現(xiàn)對于原補碼的世界觀都被刷新了……
以下摘自知乎https://www.zhihu.com/question/20458542/answer/40759880的Simon Cao回答
第一步,就像練北冥神功要先散功一樣,先把你心中對原碼,反碼,補碼的一套認識全部忘掉
第二步,正式開講
要說清這個問題,需要顛覆你對補碼的理解
首先灌輸一個新的概念叫,模
什么是“?!?,想象日常使用的鐘表,它可以顯示0~12點的時間,假設(shè)現(xiàn)在是2點鐘,請用手動撥動時針的方式將時間減4小時,你會怎么做?
有兩種方式:
逆時針將時針撥4小時
順時針將時針撥8(12-4)小時
這里要講的是第二種方式,為什么順時針撥12-4也可以達到和正常思維的第一種方式一樣的位置。
12就是模。
同樣的,如果是十進制的兩位數(shù),80-10 和 80+90在不考慮百位數(shù)的基礎(chǔ)上都是70。這里的90就是100-10得來的,這種情況下100就是模
模就好比是一個極限,在它的范圍內(nèi),兩個相加等于模的數(shù)互為補數(shù),還是舉100的例子
90和10, 55和45,68和32,互為補數(shù)
在模的范圍內(nèi)做減法,可以將“X-Y”的減法變更為“X+Y的補數(shù)“的加法,當然前提是不考慮百位數(shù)
思考題,上面舉的例子是大數(shù)減小數(shù),那么如果是小數(shù)減大數(shù)會怎么樣呢?
如果是10-80,結(jié)果應(yīng)該是-70,但如果按照10+(100-80),結(jié)果是30。
而很明顯-70和30不是一回事,這里也沒有百位數(shù)的問題,這種情況應(yīng)該怎么破?
當初的那些先賢們想出來的辦法很簡單,就是把這兩個數(shù)直接劃上等號,正好順便解決了負數(shù)的表達方式。再來仔細看看這兩個數(shù)的關(guān)系:-70絕對值的補數(shù)就正好是30
所以在計算機中,負數(shù)的表達方式就是它絕對值的補數(shù)
但是問題又來了,看起來這個解決方式很完美了,但別忘了,30他已經(jīng)代表了正數(shù)的30了,現(xiàn)在又要用來代表負數(shù)的-70,誰知道它出現(xiàn)的時候到底是代表哪個數(shù)?
為了解決這個問題,需要給這套規(guī)則劃定一個范圍,原來是0~99的正數(shù),現(xiàn)在既然要用部分正數(shù)來代替負數(shù)了,那就要規(guī)定一個范圍來使得一個數(shù)只代表一個含義,正好一人一半,0~49這個區(qū)間就代表正數(shù),50~99的區(qū)間就用來代表各自補數(shù)的負值,例:98就代表-2
第三步,現(xiàn)在回到二進制的計算機世界
8位二進制數(shù)一共可以表示2的8次方,256個數(shù),即0~255 (別忘了0也要占一位的),他們的極限就是256,即256是8位二進制數(shù)的模 ,應(yīng)該不難理解吧,同上十進制的兩位數(shù)0~99的模是100。
還是用二進制來說明清楚,8位二進制能表示的數(shù)的極限是
1 1 1 1 1 1 1 1, 就是255,在這基礎(chǔ)上加0 0 0 0 0 0 0 1,出現(xiàn)了進一位 即 1 0 0 0 0 0 0 0 0
這個1 0 0 0 0 0 0 0 0就是8位二進制數(shù)的模,256
同樣按照第二步講的邏輯,一半的數(shù)0~127,代表其正數(shù)本身,另一半的數(shù) 128~255,代表其補數(shù)的負值,即“-1~-128”的區(qū)間。
而 “X-Y”的減法 就用 “X+Y的補數(shù)”的加法來表示,完美! 唯一需要注意的事情是任何計算的輸入值和輸出結(jié)果值都需要嚴格遵守-128~127的范圍,一旦溢出就會報錯。
這也就是我們在編程里強調(diào)的為什么 byte+byte還得是byte,int+int還得是int,數(shù)據(jù)溢出問題也是每一個程序員都需要注意的問題。
這樣一說是不是可以理解-128的補碼是怎么來的了吧? 他就是256-|-128|=128
二進制的128是不是就是1 0 0 0 0 0 0 0 ?
最終問題,那書和老師為什么要用原碼,反碼來講補碼 ?
空穴來風,未必無因
那是因為計算機就是這樣求負數(shù)的補碼的,我們在鍵盤上敲一個負數(shù)的時候,計算機要把它用補碼的形式存儲下來,還記得上面我們講的補碼是怎么來的嗎?
模-絕對值,這是不是個減法公式?但計算機沒有減法邏輯,我們費了那么大的勁搞了一套補碼的規(guī)則就是為了用加法來替代減法,但為了實現(xiàn)這么套規(guī)則,卻跨不過一個坎,就是把負數(shù)計算成補碼仍然是需要減法邏輯的。怎么辦呢,那些偉大的先賢們 (膜拜)就想出了這么個辦法:
首位不變,其余位取反后,再加一
下面是吐槽
不知道是哪個書呆子教書,照搬了機器的邏輯,把取反加一的方法當做補碼的計算邏輯就這么教下來了。搞笑的是,還保留了補碼這個名字,照理說這種教法應(yīng)該叫 取反加一碼 更合理,你還補什么啊?
不僅如此,還搞出了個首位符號位的說法,弄出了個正0負0,還用負0來充當-128,真是不把人弄瘋不罷休啊??!
哈哈,確實有意思啊。為此我也找資料論證他的觀點,發(fā)現(xiàn)補碼確實很有意思啊。
參考資料:原碼、反碼和補碼的教學探討