原码补码转换|补码实现加减法操作及正确性分析
2026-09-09
原码
首先,将一个正整数转换为二进制表示时,直接将其除以 2 取余数,直到商为 0,然后将余数倒序排列即可得到该整数的二进制表示。

二进制整数通常是有限位的。对于无符号整数(即自然数),四位二进制可以表示的范围是 (0000)2 到 (1111)2,即 0 到 15。n 位二进制可以表示的无符号整数范围是 0 到 2n−1。
若要用二进制表示有符号整数,通常约定从左往右数第一位为符号位。第一位为 0 表示正数,第一位为 1 表示负数。
对于负数,要表示成原码,则只需要把符号位设置为 1,第二位及以后的位置表示其绝对值即可。
例如,6 的四位原码表示是 (0110)2;而 −6 的四位原码表示是 (1110)2。
反码和补码
反码:
- 对于正数而言,反码就是其原码本身
- 对于负数而言,反码就是其原码的符号位不变,其余各位取反
- 例如,要把 −6 表示成四位有符号整数的反码表示,是 (1001)2
- 反码这个概念本身,似乎在实际运用和课程考察中都已经不再重要,只是 CSP 初赛中有时候还会问
补码:
- 对于正数而言,补码就是其原码本身
- 对于负数而言,补码就是其原码的符号位不变,其余各位取反,最后再加 1
- 也就是其反码加 1
- 例如,要把 −6 表示成四位有符号整数的补码表示,是 (1010)2
深入理解补码
要把一个负数 x 进行“符号位不变,其余各位取反”(即获得反码)的操作,实际上也可以理解为:
- 第一位取 1
- 其余各位是用 (111...1)2 减去 x 的绝对值的结果
假设 x 原本是一个用 n 位二进制表示的有符号数,这里的 (111...1)2 是一个 n−1 位的二进制数,和 x 去掉符号位后的二进制数位数相同。
从 n−1 位无符号整数的角度来看,(111...1)2 的值是 2n−1−1。那么,负数 x 的反码对应的无符号整数值就是 (2n−1−1−∣x∣)+2n−1= 2n−1−∣x∣=2n−1+x。鉴于 x 的补码是其反码加 1,所以易得负整数 x 的补码对应的无符号整数值就是 2n+x。
在我们继续往下看之前,我们先来看看我们发现的这个神奇的原码到补码的转换方法。定义函数 f(x):
f(x)={x,2n+x,x≥0x<0
对于整数 x,f(x) 就是 x 的补码对应的无符号整数值;换言之,把 f(x) 对应的值转换为二进制表示,就是 x 的补码表示。
这里 x 的取值范围是 [−2n−1,2n−1−1],这也是用 n 位二进制数表示有符号整数的取值范围。
你可以用十进制来帮助理解
对于十进制数字,n 位数字能表示的范围是 [0,10n−1]。这里共有 10n 种可能的组合,这是因为每一位有 10 种可能的值,n 位数字就有 10n 种可能的组合。
对于二进制也是同理,但是由于我们第一位表示了符号位,所以正、负数字表示的范围都被砍半了。
补码 (1000...0) 的特殊性
你会发现,补码 (1000...0)2 对应的有符号整数值是 −2n−1,而这个值用同样位数的原码是无法表示出来的。按照原码的定义,(0000...0)2 和 (1000...0)2 都是 0,浪费掉了一个编码。
由于原码无法表示 −2n−1,我们在目前这里直接断定 f(x) 的定义域包含 −2n−1 也并不严谨。
上面这些是大多数课程在初识补码的时候会介绍的转换方法,而补码背后的实际原理是下文将会介绍的同余。使用补码 (1000...0)2 来表示 −2n−1 符合同余相关的特性,也可以正确进行运算,因而直接把 −2n−1 包含在 f(x) 的定义域中是正确的。
使用补码做加法
使用原码的情况下,正整数直接相加的结果是正确的。例如,3+2 的二进制原码表示是 (0011)2+(0010)2=(0101)2,而 (0101)2 对应的有符号整数值正是 5。
然而,我们想进行包含负数的加法是困难的。例如,1+(−1) 的二进制原码表示是 (0001)2+(1001)2=(1010)2,而 (1010)2 作为原码对应的有符号整数值是 −2,显然不正确。
为了简化计算机硬件,我们希望数字的运算可以统一,而不是为负数专门发明一种新的运算方法。而补码正可以解决这个问题。
我们接下来证明:在用补码表示的情况下,对于有符号整数 x 和 y,直接对二者的补码进行朴素的二进制相加,得到的结果就是 x+y 的补码表示。此处我们讨论的是 n 位二进制数的情况,对于运算过程中的溢出情况不做考虑。
当 x 和 y 都是正数时,补码就是原码本身,相加的正确性是显然的。
当其中有一个是负数时,假设 x 是正数,y 是负数。根据上面的推导可得,y 的补码对应的无符号整数值是 2n+y。那么,直接将 x 和 y 的补码视作无符号整数进行相加的结果就是:
Ans=x+(2n+y)=2n+(x+y)
n 位无符号整数的取值范围是 [0,2n−1],而 2n+(x+y) 对应的无符号整数值很可能超过这个范围。但这也无关紧要,因为 2n 在二进制中是一个最高位为 1,其余位为 0 的 n+1 位数,而这个数表示为 n 位二进制数的时候会溢出,最终表示就是 (0000...0)2
所以,在上式中,2n 存在的作用是,如果 x+y 的结果是负数,那么 2n 的存在会让最终的结果回到 [0,2n−1] 的范围内;而如果 x+y 的结果是正数,那么 2n 的存在不会影响最终的结果。
换言之,Ans 的值实际上就是 x+y 对 2n 取模后的结果,即 Ans≡x+y(mod2n)。写到这里你还会发现,实际上 x 和 y 有一个还是两个负数都是一样的,结果都是 x+y 对 2n 取模后的结果。
我们再把上面的 f(x) 拿下来,但是这次加入取值范围:
f(x)={x,2n+x,0≤x≤2n−1−1−2n−1≤x<0,f(x)∈[0,2n−1]
那么我们也不难据此写出 f−1(x):
f−1(x)={x,x−2n,0≤x≤2n−1−12n−1≤x≤2n−1,f−1(x)∈[−2n−1,2n−1−1]
这样我们就可以用 f−1(Ans) 来得到 x+y 的真实结果。
至此我们证明了:在用补码表示的情况下,对于有符号整数 x 和 y,直接对二者补码进行朴素的二进制相加,得到的结果就是 x+y 的补码表示。
进一步观察新的 f(x),我们不难看出,f(x) 实际上就是一个通过对 2n 取模,把 [−2n−1,2n−1−1] 映射到 [0,2n−1] 的函数。
加减法的统一
对于a−b,计算机内没有单独的减法器元件,而是改为 a+(∼b+1) 进行计算。
此处 ∼b 表示对 b 的补码按位取反操作。
让我们来证明这一转换的正确性。
我们令 b′=f(b),即 b′ 是 b 的补码对应的无符号整数值。
我们根据先前推导补码的经验不难得出:
∼b+1=((2n−1)−b′)+1=2n−b′
这里无论 b′ 的值是 b 还是 2n+b,都有:
(∼b+1)≡−b′≡−b(mod2n)
进而有:
a+(∼b+1)≡a−b′≡a−b(mod2n)
而同余 2n 意味着在 n 位二进制表示下,这三者的数值相等。
至此,减法可以通过按位取反操作,转换为加法运算来实现。
致谢
感谢 KazimierzY 在推导过程中提供的帮助。
参考资料: