首页 > 技术文章 > 原码、补码和反码分析

GodSince 2019-05-27 06:01 原文

补码是为了解决计算机的减法问题,计算的本质是取模。

 

来源:

  1. 计算机只有加法运算器,没有减法,所以引入符号位,例1001表示-1;

    注:从硬件的角度上看,只有正数加负数才算减法。

      正数与正数相加,负数与负数相加,其实都可以通过加法器直接相加。

  2.引入符号位后会出现问题(因为要用负数表示减法,所以这里符号位也有参与运算)

  例:

    0001+1010=1011 即:1+(-2)=(-3)有问题

    1001+1010=10011(溢出)=0011 即:(-1)+(-2)=3有问题

    结果:因为符号位的关系,正数相加没问题,但正负数相加和负数相加有问题,

  3.为解决第2步的问题,引入反码,

    注:正数的反码还是原码,负数的反码为除符号位外,按位取反。

  之后重新计算:

    0001(1的反码)+1101(-2的反码)=1110(-1的反码) 即:1+(-2)=(-1)没问题

    0010(2的反码)+1110(-1的反码)=10000(溢出)=0000(0的反码) 即:2+(-1)=0有问题

    1110(-1的反码)+1101(-2的反码)=11011(溢出)=1011(-4的反码) 即:(-1)+(-2)=(-4)有问题

  结果:正数相加没问题,但正负数相加和负数相加还是有问题,

    仔细观察,发现没溢出时是对的,溢出时结果就少了1;

    这样来看的话,反码已经基本解决了减法的问题,但这样还是不行。

  4.为了解决第3步的问题,引入了补码:

    注:正数的补码还是原码,负数的补码为反码+1。

  之后重新计算:

    0001(1的补码)+1110(-2的补码)=1111(-1的补码) 即:1+(-2)=(-1)没问题

    0010(2的补码)+1111(-1的补码)=10001(溢出)=0001(1的补码) 即:2+(-1)=1没问题

    1111(-1的补码)+1110(-2的补码)=11101(溢出)=1101(-3的补码) 即:(-1)+(-2)=(-3)没问题

  问题完美解决(感觉本质上就是为了不用到减法而使用补码)

 

补码的思想:

  大部分的说法:

    正数的补码等于他的原码

    负数的补码等于反码+1。

  另外一种说法:

    负数的补码等于他的原码自低位向高位,尾数的第一个‘1’及其右边的‘0’保持不变,左边的各位按位取反,符号位不变。

  其实这两种只是补码的算法而已。

 补码的实质就取模

  比如,现在是10点,如果把指针指向8点呢,答案是-2或者+10,

  这里为什么10-2=10+10=8(点)呢

  是因为(10-2)%12=(10+10)%12=8

  当10+10>12时,相当于%12;10>=0,所以10的补码为10(其实正数非要计算补码也行,反正10 mod 12=10,只是没必要);-2<0,-2的补码为(-2+12)%12=10

 

  再例如:四位二进制数中,四位二进制数只能表示0000~1111

    -1000(-8)<X<0111(7)

    编码就是X mod 16

    意思就是:非要用加法表示减法的话,可以让数溢出,即模16

    当X>0时,X mod 16 =X,即正数的补码为正数,

    当X<0时,X+16 mod 16的值就是补码,效果就是取反加一

    X=1,补码:X mod 16 =1=0001

    X=-1,补码:X+16 mod 16=15=1111

    X=-5,补码:X+16 mod 16 = 11=1011

    X=-8,补码:X+16 mod 16=8=1000

    2-1=2+(-1)=2+(-1+16)%16=2+15=17=10001(溢出)=0001=1,

    相当于一天有16个小时(由二进制的位数来决定),此时为2点,要把指针移动到1点,则可以退后一点,即-1,也可以往前推动15点(15相当于-2的补码,计算是(-1+16)%16),此时会等于17点,指针多转了一圈(相当于溢出),然后减16就等于1点了(因为位数的限制,所以任何数都要%16),因为指针只能向前不能向后(相当于计算机只有加法没有减法,这是个巧合,强行解释一下(*^_^*)),所以只能+15不能-1。这里也不是很严谨,因为不好解释1-2=(-1)。

    再比如X= -5,说明指针要退5点,但因为只能前进,一天有16小时,所以需要前进11小时:(-5+16)%16=11。

 

从数学角度来证明:

  1. 为什么补码等于反码+1

    假设有4位,以-5为例:

    -5的补码为:(-5+16)%16=16-5=10000-101=(1111+1)-101=(1111-101)+1=1011

    其中1111-101相当于求-5的补码(符号位一定为1,所以符号位不变),最后再+1。

  2. 负数的补码的补码为什么等于本身:

    证明1:

      还是假设有4位,设负数的数值位为X,

      则X的补码为:(-X+16)%16

      X的补码的数值位为(-X+16)%16-8;

      X的补码的补码的数值位为(-((-X+16)%16-8)+16)%16-8

      通过计算可得X=(-((-X+16)%16-8)+16)%16-8,即补码的补码的数值位保持不变;

    证明2:

      取-0=100000000……,记住10*,其中0*表示有0到无穷个0,1*表示有0到无穷个1,

      在数值位上随机取几个1(假设为3个吧),则负数可表示为:10*10*10*10*,

      原码:10*10*10*10*,

      反码:11*01*01*01*,

      补码:11*01*01*10*,

      补码的反码:10*10*10*01*,

      补码的补码:10*10*10*10*,

      从这边就可以看出,前面三个部分补码的情况和反码是一样的,最后一部分的变化为:10*(原码)→01*(反码)→10*(补码)→01*(补码的反码)→10*(补码的补码)=10*(原码)

  3. 为什么负数的反码运算溢出时会少1:

    略(以后有空再证明吧,似乎蛮难的但也不重要,就当作溢出的1应该进位到末位吧)

 

 

推荐阅读