上文「原码 反码 补码 移码」一探究竟(一)说了基本定义和原码,对于补码,我们只知道是对原码符号位不变,其他位置取反,最后再加 1 得来的,为何如此呢?接下来咱们来揭下「补码」的面具,看看它到底是什么。
0. 关于 1 + (-1)
首先,先看一个问题。
1 的原码为[0000 0001],-1 的原码为[1000 0001],所以计算这两个数相加,应该是这样的:
1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [1000 0010]原
= -2
复制代码
结果竟然是 -2,很明显是错的,这样用原码计算就出问题了。当然,劳动人民的智慧不可估量,总能发现合适的方式来解决各种问题,于是,补码就诞生了,再看用补码计算的过程。
1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [0000 00001]补 + [1111 1111]补
= [0000 0000]补
= [0000 0000]原
= 0
复制代码
结果正确,问题得以解决,而这也是计算机都是以补码的形式来存储整数的原因。
但是,为什么用补码计算就能得到正确结果呢?为什么补码的计算方式是原码取反再加 1 呢?带着问题,我们继续往下看。
1. 钟表上的哲学
钟表,每个人应该都清楚的,上面的数字范围[0,11],也可以理解为[1, 12],毕竟上面没有写 0 这个数字,但是不变的是,都可以表示12个小时。
比方说,现在是 9:00,时针指向9。我要想知道 7 个小时之前是几点,那么我只需要将时针向回拨动 7 个格子即可,结果很显然,时针将会指向 2,表示 2:00;但是,我要想知道 5 个小时后是几点呢?也很简单,将时针向前拨动 5 个格子,结果也很显然,时针也会指向 2,表示 2:00。
通过不同方式,我们得到了同样的结果,也就是说在钟表上,9 – 7 = 9 + 5 = 2。不仅 7 和 5 有这样的规律,8 和 4、9 和 3等都有这样的规律,也就是说,相加等于 12 的两个数都符合这样的规律,即 X – Y = X + (12 -Y),而 12 在这里有个名字,叫做这个钟表的模,12 – Y 叫做 Y 的补数。
减去一个数,等于加上这个数的补数,应用这个规律就可以将减法转换为加法了。
那么问题来了,模长该怎么求?
2. 通俗的「模」
通俗的讲,很简单。还是拿钟表举例,上面能表示的数字的总数就是其模长,所以不管是[0, 11],还是[1, 12],都为12。
再来看 8 位二进制,其原码能表示的范围 (注意看,这里说的是原码),[1111 1111] ~ [0111 1111],即 [-2^7 – 1, 2^7 – 1] = [-127, 127],因为我们是要将其全部转变为非负数,即能表示的范围为[0, 127],所以模长为 128。
说完了这些,我们再来重新看下 -3 的补码的计算过程。-3 原码为 [1000 0011],而取反的过程实际上等同于用[0111 1111]减去 -3 原码中的符号位之外的部分,之后再加 1 即得到补码,所以:
-3 补码(未添加符号位)
= [1000 0011]原 取反 + [0000 0001]
= [0111 1111] - [0000 0011] + [0000 0001]
= [0111 1111] + [0000 0001] - [0000 0011]
= [1000 0000] - [0000 0011]
= 128 - [0000 0011]
= 模 - [0000 0011]
= 模 - 3
复制代码
看到这,是不是一下就明白了?补码实际上就是模减去原码的值,再加上一个符号位,也就是所说的:符号位不变,取反再加1。
所以,在计算机中,整数都是以补码的形式存储的,是为了统一加减法运算。因为计算机之中是没有做减法的逻辑门,减法都会被转化为加法来完成计算。
而通过溢出,符号位也可以直接参与计算,大大简化了计算过程,看个例子就明白了。
7 + (-3)
= [0000 0111]原 + [1000 0011]原
= [0000 0111]补 + [1111 1101]补
= [0000 0100]补 (溢出部分不用处理)
= [0000 0100]原
= 4
2 + (-3)
= [0000 0010]原 + [1000 0011]原
= [0000 0010]补 + [1111 1101]补
= [1111 1111]补
= [1000 0001]原
= -1
复制代码
再看个特例。
-1 + (-127)
= [1000 0001]原 + [1111 1111]原
= [1111 1111]补 + [1000 0001]补
= [1000 0000]补
= -128
复制代码
用原码能表示的最小负数为 -127,补码却能表示的最小负数为 -128,但是 -128 没有原码和反码表示,由于计算机中使用补码表示整数,所以这没有影响,因此 8 位二进制数,也就是 byte 类型能表示的范围是 [-128, 127]。
说到这,对于补码,应该足够清晰了吧!
欢迎关注同名公众号「码一八」获取更多内容,用技术改变生活!
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/101163.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...