之前一直搞不明白計算機中負數該如何存儲,涉及到負數的問題,想起以前關于負數補碼的知識看過但沒通透,今天把它弄明白。看了些補碼的知識,摘抄了些,自己整理了些。
順便帶著兩個小問題:
INT_MAX = 2147483647 ;
cout<<INT_MAX+1<<endl; //正確結果為-2147483648
UINT_MAX = 4294967295;
cout<<UINT_MAX+1<<endl; //正確結果為0
開始正文:
1、負數在計算機中如何表示?
舉例來說,+8在計算機中表示為二進制的1000,那么-8怎么表示呢?
很容易想到,可以將一個二進制位(bit)專門規定為符號位,它等于0時就表示正數,等于1時就表示負數。比如,在8位機中,規定每個字節的高位為符號位。那么,+8就是00001000,而-8則是10001000。但是,隨便找一本《計算機原理》,都會告訴你,實際上,計算機內部采用2的補碼表示負數。
2、什么是2的補碼?
它是一種數值的轉換方法,要分二步完成:
第一步,每一個二進制位都取相反值,0變成1,1變成0。比如,00001000的相反值就是11110111。
第二步,將上一步得到的值加1。11110111就變成11111000。
所以,00001000的2的補碼就是11111000。也就是說,-8在計算機(8位機)中就是用11111000表示。
不知道你怎么看,反正我覺得很奇怪,為什么要采用這么麻煩的方式表示負數,更直覺的方式難道不好嗎?
3、為什么要用2的補碼
首先,要明確一點。計算機內部用什么方式表示負數,其實是無所謂的。只要能夠保持一一對應的關系,就可以用任意方式表示負數。所以,既然可以任意選擇,那么理應選擇一種方便的方式。
2的補碼就是方便的方式。它的便利體現在,所有的加法運算可以使用同一種電路完成。
還是以-8作為例子。
假定有兩種表示方法。一種是直覺表示法,即10001000;另一種是2的補碼表示法,即1 1111000。請問哪一種表示法在加法運算中更方便?隨便寫一個計算式,16 + (-8) = ? 1 0001000 取反 1 1110111 加1 +1 = 1 1111000 再取反 1 0000111 + 1 = 1 0001000 取反不不包括符號位兩次取反得到原值正數的補碼是其本身 負數的補碼是符號位不變 其他位取反之后加1 連著變換兩次相當于沒有做任何操,16的二進制表示是 00010000,所以用直覺表示法,加法就要寫成:
00010000
+10001000
---------
10011000
可以看到,如果按照正常的加法規則,就會得到10011000的結果,轉成十進制就是-24。顯然,這是錯誤的答案。也就是說,在這種情況下,正常的加法規則不適用于正數與負數的加法,因此必須制定兩套運算規則,一套用于正數加正數,還有一套用于正數加負數。從電路上說,就是必須為加法運算做兩種電路。
現在,再來看2的補碼表示法。
00010000
+11111000
---------
100001000
可以看到,按照正常的加法規則,得到的結果是100001000。注意,這是一個9位的二進制數。我們已經假定這是一臺8位機,因此高的第9位是一個溢出位,會被自動舍去。所以,結果就變成了00001000,轉成十進制正好是8,也就是16 + (-8) 的正確答案。這說明了,2的補碼表示法可以將加法運算規則,擴展到整個整數集,從而用一套電路就可以實現全部整數的加法。
綜上,對剛開始的兩個小問題的解釋:
(1)32位系統里,int的大值為01111111 11111111 11111111 11111111,加1之后為
10000000 00000000 00000000 00000000。這個數是什么?
首先這是個負數-->負數在計算器里都是補碼形式存放-->這是個補碼-->那么真值是?--> -2147483648(已知負數的補碼求該負數,不會求的百度一下吧。。。)
(2)再取反就得到原來的值,對于unsigned,大值(32個1)加1后前面的1自然丟失,剩下32個0,所以就是0。
結束。