由于计算机位数的限制,在一般的编程语言中,整数所表示的范围都是有限的。java中,整数是32位,所以最多能表示2^32个数。人类最容易想到的就是原码的表示方式:第一位数用来表示正负,后面的位数用来表示值,表示的范围为-(2^31-1) ~
(2^31-1)。
这种表示方式对于人类的理解非常友好。但是,对于计算机来说怎么样呢?这种编码方式下怎么进行加减计算呢?
a+b(a,b可正可负,所以加减法情况都包括了)的计算可以分为以下两种情况进行讨论:
1)a * b > 0:即ab同为正或同为负。那么需要保持符号位不变,数值位直接相加。向上溢出(结果大于
2^31-1)会从+0开始重新循环,向下溢出(结果小于-
(2^31-1))会从-0开始循环。
2)a * b < 0:即ab异号。那么需要先判断ab的数据谁大。决定结果是正还是负。然后数值位大的送去数值位小的。
这.....是不是太麻烦了一点(
以上是个人分析,不保证是最优方法),如果所以计算机进行的加减操作都需要进行这么多种判断,那效率实在是堪忧。再者,当发生溢出时,我们期望的规则是什么?当然,我们期望的溢出规则是像时钟一样,从12向上溢出变为0,从0向下溢出变为12。
那么有没有一种编码方法,可以保证满足我们的溢出规则,同时又保持高效呢?答案是肯定的,这就是我们的补码。下面我将按照我的理解分析分析补码为什么高效。
三、补码有啥好处?
整数最大值:01111111 11111111
11111111 11111111 加1,变成了:10000000 00000000 00000000 00000000。我们希望加1能够向上溢出表示最小值,那么让10000000 00000000 00000000 00000000这个值表示最小值如何呢?取消之前的符号位定义,将第一位定义成:-k*2^31(k表示第一位的数值,为0或1)。后面的每一位都跟原码含义一样。这样,上界加1溢出到下界,下界减1溢出到上界,貌似很完美,那么进行加法运算呢?以a+b来进行说明。
1) a>=0&& b>=0,所有位直接相加即可得到正确结果。(如果发生溢出则上界溢出到下界)
2) a>=0 && b< 0。又分为|a| > |b|和|b|>|a|两种情况。
|a| > |b| 直接相加。 a低31位的值为|a|,b的低31位值为2^w-|b|。因为|a|>|b|,所以低31位相加会发生溢出。所以最高位结果最终为0,低31位结果为|a|-|b|。跟真实结果相符。
|a| < |b| 直接相加。 a低31位的值为|a|,b的低31位值为2^w-|b|。因为|a|<|b|,所以低31位相加不会发生溢出。所以最高位结果最终为1,低31位结果为2^31-|b|+|a|。跟真实结果相符。
3)a<0 && b<0
|a| + |b| < 2^31。直接相加。 a低31位的值为2^31-|a|,b的低31位值为2^31-|b|。因为 |a| + |b| < 2^31,所以2^31-|a|-|b|不会溢出。将其中一个2^31向高位进1,所以最高位结果最终为1,低31位结果为2^31-|a|-|b|。跟真实结果相符。
|a| + |b| > 2^31。直接相加。 a低31位的值为2^31-|a|,b的低31位值为2^31-|b|。因为 |a| + |b| > 2^31,所以2^31+2^31-|a|-|b|才不会溢出,0<2^31+2^31-|a|-|b|<2^31。所以最高位结果最终为1,低31位结果为2^31+2^31-|a|-|b|。最终结 果为正。跟真实结果相符。
所以,最最重要的结论出现了。补码的加减法运行全部都可以当做按位相加的加法来进行运算,不管数的正负,也不管结果的正负,不需要关心最高位是0还是1,只要用补码表示,就能得到正确结果。天啦,简直太神奇了,太完美了。
四、补码是咋发明的?
这个真不知道。也许就是这么凑出来的,也许背后有着精妙的数学逻辑,也许补码的正确性也有简洁而又美的证明。这些,等我以后学到了再来补充^_^。