补码是为了解决计算机的减法问题,计算的本质是取模。
来源:
- 计算机只有加法运算器,没有减法,所以引入符号位,例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应该进位到末位吧)