补码诞生的背景
不论是在生活中还是虚拟网络中,人们总是习惯与10进制数字打交道,很容易理解10进制的加减乘除运算,但是我们知道计算机无法直接理解10进制,只能识别高低电平,一般人为设定0为低电平,1为高电平,所以又称计算机是二进制的。在计算机发展早期,人们要想使用计算机,只能使用计算机看懂的二进制与计算机打交道,如穿孔纸带,人们使用穿孔纸带将程序和数据转换为二进制码,带孔为1,无孔为0,计算机读取并处理完成后,同样在纸带上以二进制打孔输出计算结果,一般人很难操作这种早期计算机,只能是专业人士才能处理二进制的转换。随着科技的进步,普通人也能熟练的操作计算机,表面上似乎计算机已经理解了10进制,但是实际上,计算机最底层还是二进制的,这就需要10进制到二进制的自动转换以及使用二进制进行各种运算。
10进制到二进制的转换需要考虑两方面,第一个是编码格式,二进制中只有0和1,不同于常用的10进制使用 + 或者 - 符号代表正负数,要想让二进制只使用1和0代表正负数,需要找到合适的编码格式;第二个是运算,以加减运算为例,计算机内部是比较复杂的,计算机实现加法运算是很容易的,若直接作减法则比较复杂,需要处理借位等等,内部逻辑组件会增多,所以计算机一般在减去一个数的时候会转成加上这个被减数的负数,将减法转换成了加法,即A - B = A + (-B)。所以需要找到一种满足这两个方面的编码,目前10进制转换成二进制主要有三种方式:原码、反码、补码,下面将从编码格式和运算两方面对比这三种编码格式。
原码、反码、补码
原码
原码是最简单也是最直观的从10进制到二进制的编码格式,人为规定原码的最高位为符号位,正数为0,负数为1,其余所有位为10进制数的绝对值。如下面例子:
10进制 | 二进制原码(4位) |
---|---|
4 | 0 100(首位为0,代表为正数) |
2 | 0 010(首位为0,代表为正数) |
0 | 0 000(首位为0,代表为正数) |
-2 | 1 010(首位为1,代表为负数) |
-4 | 1 100(首位为1,代表为负数) |
十进制和二进制原码转换
原码的优点是编码格式对人很友好,类似十进制中的正负号,原码用最高位0和1分别代码正负数,很直观的表示了正负数。但是原码也有一个很大的缺点,就是无法将减法转换成加法运算,如:
4 - 2 (10进制)= 4 + (-2)= 0 100 + 1 010 (二进制原码) = 1110 (二进制原码)= -6 (10进制)
上面例子计算4-2,将4-2转换成4+(-2)并用原码计算,得出的结果错误,原码虽然很直观转换了10进制数,但是计算输出的原码值并不正确,所以计算机不能直接使用原码存储和计算。
反码
反码的出现,主要是为了解决原码无法执行减法运算的问题,人为规定反码最高位为符号位,正数为0,负数为1,反码正数与原码正数格式一致,反码负数为负数绝对值的原码按位分别取反,如下面例子:
10进制 | 二进制原码(4位) | 二进制反码(4位) |
---|---|---|
4 | 0 100(首位为0,代表为正数) | 0 100(首位为0,代表为正数) |
2 | 0 010(首位为0,代表为正数) | 0 010(首位为0,代表为正数) |
0 | 0 000(首位为0,代表为正数) | 0 000(首位为0,代表为正数) |
-2 | 1 010(首位为1,代表为负数) | 1 101(首位为1,代表为负数) |
-4 | 1 100(首位为1,代表为负数) | 1 011(首位为1,代表为负数) |
十进制和二进制反码转换
反码的负数编码格式不像原码那样直观,但是却可以将减法转换成加法了,反码减法规则为:A - B = A + (-B),如果最高位发生了溢位,则需要在最低位加上1,如下面两个例子:
1)4 - 2 (10进制)= 4 + (-2)= 0 100 + 1 101 (二进制反码) = 1 0001 (二进制反码,发生了溢位)= 0001 + 0001(最低位加1) = 0010 (二进制反码)= 2(10进制)
2)2 - 2 (10进制)= 2 + (-2)= 0 010 + 1 101 (二进制反码) = 1111 (二进制反码)= - 0 (10进制)
运用反码减法规则,得到的上面两个例子的减法结果是正确的,所以计算机是可以使用反码存储和计算的,早期的计算机如CDC 6000、LINC、PDP-1等都是使用反码的,但是反码也有两个缺点:
1)0有两种编码,+0 (0000)和 -0 (1111),在判断0时,需要分别判断0000和1111;
2)反码减法的算法规则比较复杂,需要增加计算机内部逻辑组件额外判断溢位,会影响计算效率。
补码
补码是现代计算机使用的编码格式,解决了上面反码的两个缺点。正数的补码与原码格式相同,负数的补码是将负数绝对值的原码分别按位取反,并加1,如下面例子:
10进制 | 二进制原码(4位) | 二进制补码(4位) |
---|---|---|
4 | 0 100(首位为0,代表为正数) | 0 100 |
2 | 0 010(首位为0,代表为正数) | 0 010 |
0 | 0 000(首位为0,代表为正数) | 0 000 |
-2 | 1 010(首位为1,代表为负数) | 1 110 |
-4 | 1 100(首位为1,代表为负数) | 1 100 |
十进制和二进制补码转换
补码的减法规则比较简单,按照最简单的转换公式A-B = A + (-B),当减去一个数时直接转换成加上被减数的负数即可,不用像反码那样额外处理溢位,如下面两个例子:
1)4 - 2 (10进制)= 4 + (-2)= 0 100 + 1 110 (二进制补码) = 1 0010 (二进制补码,发生了溢位,直接丢弃溢位)= 0010(二进制补码) = 2(10进制)
2)2 - 2 (10进制)= 2 + (-2)= 0 010 + 1 110 (二进制补码) = 1 0000(二进制补码,发生了溢位,直接丢弃溢位)= 0000 (二进制补码) = 0(10进制)
使用了补码的加法,上面两个例子得出的结果都是正确的,相对于反码,补码加法更简单,直接丢弃溢位,不需要针对溢位单独处理,所以用补码做运算效率高。虽然补码运算过程很简单,但是转换和运算规则却很难理解,要弄明白其中的原理,就需要揭开补码背后的数学奥秘。
加减法
普通算术加减法
普通算术加减法是我们在生活中一直使用的,也是最简单和最容易理解的,通常人为使用 + 和 - 符号规定正负数,正数通常省略 + 符号, 如果10,20,-10,-20,正负数的加减运算则可以看成是一维运算,如下图:
上图是普通算术加减法示意图,当执行加法运算时,需要向右移动,比如0+3,在0位置向右移动3位,即为0+3的结果;同样的,当执行减法运算,向左移动。向左和向右是没有尽头的,可以一直移动到正的无穷大或者负的无穷大。普通算术加减法简单直观,很容易被人理解,但是对于计算机要实现这样的算术加减法,既要区分正负数,又要分别实现加减法,设计就会很复杂,效率会很低,所以这套算术加减法并不适用计算机。所以需要找到一种不需要区分正负数就可以实现加减法转换的规则,那么计算机运行效率就会最高。
模N加减法
模N加减法正是不需要区分正负数就可以实现加减法转换的运算方式,不同于普通算术加减法,它是二维运算,要理解模N加减法比较困难,可以先用生活最典型的时钟举例,如下图:
上面是生活中常见的时钟,如果当前时间为凌晨1点,要知道5个小时之后的时间是多少,只需要顺时针旋转5格,指向了6点,即为1+5的结果;如果想知道5个小时之前的时间是多少,需要逆时针旋转5格,指向晚上8点,即为1-5的结果。时钟顺时针相当于时间向前走,逆时针相当于时间往后走,但是时钟不会指向无穷大的数,当转过24个小时(24小时制)又回到了原点。在时钟转动中,1-5的最终结果为晚上8点,逆时针旋转5个小时就可以得到正确结果,同时也可以顺时钟旋转19个小时(24-5,24小时制),两种方式旋转都最终指向了晚上8点。所以任意逆时针旋转得到的结果都能通过顺时钟旋转得到,当逆时针旋转N个小时,与顺时针旋转24-N小时相等,24又称为模,如果把顺时针看成是加法,逆时针看成是减法,那么时钟旋转可以看成模24的加减法运算,满足公式A-m=A+(24-m),即在时钟任一时刻A点,从A点逆时针旋转m个小时得到的结果,与从A点顺时针旋转24-m得到的结果一致。模N运算将减法转换成了加法。
计算机使用的二进制位数是有限制的,比如4位,8位,16位,64位等等,当数值太大超过最大位数时,会发生溢出,重新归0,所以计算机的二进制能表示的数不是无穷大的,由于溢出归零的特点,更像时钟旋转,如下图:
上图的四位二进制表示了从0000-1111,当超出1111时,四位已无法表示,会发生溢出,高于四位的位会被丢掉,比如1111加上2等于10001,10001包含五位二进制,最高位1会被丢掉,实际结果为0001,与时钟运算很相似,相当于在1111顺时针旋转了2个数。在时钟运算中,将顺时针看成加法,逆时针看成减法,那么时钟运算可以看成是模24的加减法,同理四位二进制也可以看成是模N的加减法,在4位二进制中,转一圈为2^4=16,所以4位二进制的加减法为模16的加减法,减法很容易的就被转换成了加法,即满足模N加减法公式:A-m=A+(16-m)。
虽然根据模N加减法实现了加减法转换,但此时又有新的问题,4位二进制只有1和0,是没有区分正负数的,而人们在计算的时候是要区分正负数的,所以需要人为将部分二进制划分为负数,另一部分划分为正数,根据模N加减法公式A-m=A+(16-m),当A为零点时,根据模N加减法公式得到 0-m=0+16-m,即-m=16-m,即将零点A逆时针移动m得到负数m,同时这个负数m也可以从零点A顺时针移动16-m得到,零点A可以为上面四位二进制任意一位,比如定义0000或者0001为零点都是可以的,但是为了简单运算,人为规定0000为零点,0000逆时针方向的为负数,顺时针方向的为正数。如在0000逆时针旋转1个数或者顺时针旋转16-1得到1111,那么1111代表-1,相应的顺时针移动一个数为+1,即用0001表示+1;同理在0000逆时针旋转2个数或者顺时针旋转16-2得到1110,那么1110代表-2,相应的顺时针移动两个数为+2,即用0010表示+2;同理1101和0011分别为-3和+3,1100和0100分别为-4和+4,1011和0101分别为-5和+5,1010和0110分别为-6和+6,1001和0111分别为-7和+7,但是1000比较特殊,0000逆时针旋转8个数得到1000,所以1000为-8,相应的顺时针旋转8个数也得到了1000,1000既能表示-8又能表示+8,为了不产生冲突,人为规定1000为-8。如下图:
上图中的四位二进制根据模N加减法划分出了正负数,同理对任意n位二进制,模N等于=2^n,根据上面的模N加减法公式得到-m = N - m = 2^n - m = (2^n -1) - m + 1,最终得到了负数推导公式-m = (2^n -1) - m + 1,(2^n-1)-m即为负数的原码绝对值按位取反,之后再加上1可以快速得到负数编码,又称这种负数编码为补码。补码负数范围转换成10进制为 -1 ~ -2^(n-1),正数范围转换成10进制为 0 ~ 2^(n-1)-1,所以补码转换成10进制为 -2^(n-1) ~ 2^(n-1)-1。
总结
要想弄清楚补码,必须要弄清楚补码要解决的问题,计算机是二进制的,无法直接表示正负数,另外在计算机内部直接实现减法,也会影响计算机效率,所以人们希望要找到一种既能使用二进制表示10进制正负数的编码格式,同时这种编码格式又能满足将减法转换成加法进行运算,同时满足这两个条件有反码和补码,但由于反码中的0有两个编码格式,另外反码加法运算也比较复杂,慢慢地反码被淘汰了。补码刚好解决了反码的两个缺点,所以补码成了现代计算机的通用编码。
补码加减法运算不同于常规的算术加减法,补码使用了模N加减法,要想完全理解补码,首先要理解模N加减法。
标签:10,反码,二进制,补码,加减法,原码 From: https://www.cnblogs.com/qq53462202/p/16821375.html