计算机中用补码表示一个数是为了方便运算的,这样减法也可以通过加法来实现。为什么这样设计行呢?主要是因为计算机里数的特殊存储格式决定的,计算机里用固定位数表示一个数,超过该位置的部分会被舍弃。
拿8BYTE的int型来说,无符号形式下,最大到1111 1111 = 255,超过了该范围,会被截断的,如:111 1111 1111 –> 1111 1111 = 255,故表示范围是0~255。这样的话,减去一个数可以通过加上某个数撑出界后截断来处理。无符号形式下,拿两个正数来举例,并且是大数减小数,如38 – 37。
数学中是这样描述的:减去一个数等于加上该数的相反数,即38 +(-37)。那-37怎么表示呢?考虑37 +(-37) = 0, 37 = 0010 0101,加上-37能撑到1 0000 0000就行了。
现在引进反码的概念,即按位求反,37的反码是 1101 1010,与原码相加,变成1111 1111。再加上1的话就正好撑出界 ,是1 0000 0000,截位后为0。故-37 = 1101 1010 + 1 = 1101 1011。38 =0010 0110 ,加上1101 1011后为1 0000 0001 = 1,借助截位达到减的效果。
当然无符号情况下不能表示负数的。上述-37 = 1101 1011,其实是219。故将最高位设为符号位,1表示负数,0表示正数。
这样8Byte的int型表示的最大正数是0111 1111 = 127,那负数是怎么表示的?才能满足减法也能通过加法来实现。是不是1000 0001 就是- 1!这样不合适,拿2 – 1 = 2 + (-1)来说,0000 0010 + 1000 0001 = 1000 0011 , 如果是那样表示的话,应该是-3。
其实将无符号情况拿过来直接用就行了,-37 = 1101 1011。这样运算就方便了,直接加就行了。那为什么拿过来,负数的最高位正好是1呢?其实看看正数的表示范围就可理解了,有符号正数只能表示0000 0001 ~ 0111 1111,若想通过撑到1 0000 0000截断的方式求相反数(负数),最高位必须是1。都是0000 0001 ~ 0111 1111范围的两个数怎么加都在1111 1110以下。且对于0000 0001 ~ 0111 1111范围的任何数,都存在最高位是1的8位数(二进制)和它相加撑到1 0000 0000。0就不用求相反数,和本身一样,故放在正数里。
当然这只是运算上方便,并不是干什么都方便,显示出来时就得转换了。那补码情况下数是怎么显示的呢?首先判断符号位,符号位为0就是正数,如0111 1111,那就是127,和原码一样。符号位为1是负数,须转换,如1111 1111,那考虑它是怎么来的,是通过绝对值的原码求反加1,那反过来就是减一求反,1111 1111 -> 1111 1110 -> 0000 0001 = 1, 故1111 1111 的值是-1。
考虑求反加一和减一求反是一样的,其实补码就是这样一个数:与原码相加后正好撑出界,截断后为0,即相加为1 0000 0000 = 0,用公式表示就很容易理解了。a是原码,b是a的反码,c = b + 1是a的补码,a + b + 1 = 1 0000 0000,即a + c = 1 0000 0000, 从无符号的角度考虑,a和c互为补码,知道c求a可不原路返回,将c看成原码,求反加一得a。
再说说有符号数的表示范围吧!非负数只能表示0~127,即0000 0000 ~ 0111 1111,那负数的范围呢?负数是1000 0000 ~1111 1111,即-128~-1。故整个表示范围是-128~127。
总的来说,补码是为了运算方便,显示时须转换。
以上我的拙见,欢迎大家评论!