我们发现补码运算就很简单了,产生的进位直接舍去,而且不做多余的操作也解决了进位的问题。还有 +0 和 -0 的表示,在原码和反码都有两种形式,但是补码却只有一种:
就这样我们完美的解决了计算机中整数运算的问题。计算机的机器数采用补码的形式,我们在做算术运算的时候,既不需要额外的判断,又能得到准确的结果。
看上去本文应该结束了,然而......
请求出 127+1 的值
4、溢出
接着上面抛出的问题,127+1的值,我们现在程序中看看:
byte在计算机正好是一个字节,也就是8位二进制序列。我们发现127+1结果不是128,反而是-128,这就是结果发生了溢出。因为byte表示数的范围是-128-127,128超出了这个范围。用补码计算如下:
我们发现这个数的符号位没有发生进位,但是数值最高位发生了进位。在看前面的2-1
这个表达式符号位和数值最高位发生了进位,但是结果却是正确的。总结如下:
只有一个高位进位或者符号位进位就为溢出的规则。
溢出是每种编码在运算时都不可避免的,一般来讲结果超过字长所表示数的范围都会发生溢出。而判断机器是正常进位还是溢出的基本依据,在微型机中可用异或电路来实现上述的判断。在实际编码中解决办法也很简单,就是将结果用更大范围的编码形式接收即可。比如两个byte类型的数相加,我们用 int 来接收即可。
所以我们可以说用补码进行运算,在不考虑溢出的情况下,结果都是正确的。确实也是这样,但是......
请求出 -128 的补码?
5、剧情反转
上面的给出的问题,-128 的补码,我们首先想到去求它的原码,嗯,原码应该是 [1000 0000],不对,第一位不是符号位吗,[1000 0000]应该表示 -0。那应该怎么用原码表示 -128呢,我们发现字长为 8 的计算机用原码是无法表示的,反码也是一样。我们看看补码,用 -127- 1 的表达式结果来计算 -128 的补码:
-128的补码形式为 [1000 0000],我们能通过算术表达式得到某个数的补码形式,但是为什么直接就求不出来?那么计算机自己是怎么实现的呢?
再来看这样一个问题:我们日常使用的钟表,比如现在钟表指向的是 10点钟,我要将钟表调整到 6 点钟,则有两种拨法:
①、顺时针将时针拨动 8 格
②、逆时针将时针拨动 4 (12-8) 格
由此给大家普及一个概念叫 “模”,钟表便是一个典型的模运算系统,其模数为12。
同理,对于十进制两位数,在将结果百位舍掉的情况下,50可以用60-10得到,或者60+90得到。这里的90也就是100-10得来的,那么我们就说十进制两位数运算系统的模数为100。
我们判定:两个相加等于模的数互为补数。
在模表示的范围内做减法运算,可以将“X-Y”的减法变更为“X+Y的补数“的加法,当然这里不考虑结果溢出。
上面我们举的例子都是大数减小数,如果是小数减大数会怎样?
如果是10-80,结果应该是-70。但是如果按照 10+(100-80)的说法,结果是30。很明显,30和-70不是同一个结果,而且也没有产生百位进位。那我们应该怎么办呢?
解决办法很简单,就是让这两个数相等,而且这正好解决了负数的表示方法,-70的绝对值的补数正好是30。
但是问题又来了,这里的30已经表示正数30了,现在又表示负数-70,那我们怎么知道它到底表示哪个数?
为了解决这个问题,我们给这套规则规定一个范围,原来是0~99的正数,现在既然要用部分正数来代替负数了,那就要规定一个范围来使得一个数只代表一个含义,正好一人一半,0~49这个区间就代表正数,50~99的区间就用来代表各自补数的负值,例:98就代表-2
所以0-99的编码数可以表示的数的范围为 -50-49。
我们解决了十进制两位数的减法运算,那么在字长为 8 的计算机系统中,我们又该如何呢?
8位二进制数可以表示的数为2的8次方,0-255,一共 256 个数,0也要占用一位数。所以我们说 256 是8 位二进制数的模,这和上面说的十进制两位数0-99,模为100是一样的。
我们按照前面讲的逻辑,一半的数0-127,代表其正数本身,另一半的数128-255表示其补数的负值,即-1~-128。
所以而 “X-Y”的减法 就用 “X+Y的补数” 的加法来表示,即将减法的形式转换为加法的形式了,而且计算结果还是正确的。
注意:这里还是一样,不考虑结果的溢出,也就是计算值和结算结果都必须在-128~127之间,一旦超过这个范围,结果就不准了,这也是程序员日常编码说的int=int+int,如果结果大于int类型表示的范围,那得出来的结果肯定不准。
由此我们得出来的结论是:
计算机编码其实并没有什么所谓的符号位,但是由于计算机没有减法运算,为了将负数变为某个可以运算的编码来进行加法运算,补码产生了。这也间接说明了正数的补码是不变的,而负数的解决办法是首位不变,其余的取反再加1。
我们上面说的补码怎么得来的,就是 模-绝对值 。
所以这个时候我说让你求 -128的补码,你马上就会知
256 - |-128|=128 而128的二进制补码是不是就是 [1 0 0 0 0 0 0 0]
让你求 -1 的补码,你马上就会知
256 - |-1| = 255,其255的二进制补码形式就是[1111 1111]
注意:关于这样求补码的具体数学证明,请参考《计算机组成与系统结构》。
6、总结
指北君画重点:本篇文章你可以直接从第 5 点开始看,忘掉计算机编码的什么首位符号位,忘掉计算机补码是由原码取反加1,回归简单直白的理解。计算机是机器,硬件能理解的只有高低电平,也就是0或者1。它知道什么是符号位吗?这些规则只不过是为了更好的完成减法运算所yy出来的。
我是指北君,操千曲而后晓声,观千剑而后识器。感谢各位人才的:点赞、收藏和评论,我们下期更精彩!