Skip to content

原码补码转换|补码实现加减法操作及正确性分析

2026-09-09

原码

首先,将一个正整数转换为二进制表示时,直接将其除以 22 取余数,直到商为 00,然后将余数倒序排列即可得到该整数的二进制表示。

二进制整数通常是有限位的。对于无符号整数(即自然数),四位二进制可以表示的范围是 (0000)2(0000)_2(1111)2(1111)_2,即 001515nn 位二进制可以表示的无符号整数范围是 002n12^n-1

若要用二进制表示有符号整数,通常约定从左往右数第一位为符号位。第一位为 00 表示正数,第一位为 11 表示负数。

对于负数,要表示成原码,则只需要把符号位设置为 11,第二位及以后的位置表示其绝对值即可。

例如,66 的四位原码表示是 (0110)2(0110)_2;而 6-6 的四位原码表示是 (1110)2(1110)_2

反码和补码

反码:

  • 对于正数而言,反码就是其原码本身
  • 对于负数而言,反码就是其原码的符号位不变,其余各位取反
    • 例如,要把 6-6 表示成四位有符号整数的反码表示,是 (1001)2(1001)_2
  • 反码这个概念本身,似乎在实际运用和课程考察中都已经不再重要,只是 CSP 初赛中有时候还会问

补码:

  • 对于正数而言,补码就是其原码本身
  • 对于负数而言,补码就是其原码的符号位不变,其余各位取反,最后再加 11
    • 也就是其反码加 11
    • 例如,要把 6-6 表示成四位有符号整数的补码表示,是 (1010)2(1010)_2

深入理解补码

要把一个负数 xx 进行“符号位不变,其余各位取反”(即获得反码)的操作,实际上也可以理解为:

  • 第一位取 11
  • 其余各位是用 (111...1)2(111...1)_2 减去 xx 的绝对值的结果

假设 xx 原本是一个用 nn 位二进制表示的有符号数,这里的 (111...1)2(111...1)_2 是一个 n1n-1 位的二进制数,和 xx 去掉符号位后的二进制数位数相同。

n1n-1 位无符号整数的角度来看,(111...1)2(111...1)_2 的值是 2n112^{n-1}-1。那么,负数 xx 的反码对应的无符号整数值就是 (2n11x)+2n1=(2^{n-1}-1-|x|)+2^{n-1}= 2n1x=2n1+x2^n-1-|x|=2^n-1+x。鉴于 xx 的补码是其反码加 11,所以易得负整数 xx 的补码对应的无符号整数值就是 2n+x2^n+x

在我们继续往下看之前,我们先来看看我们发现的这个神奇的原码到补码的转换方法。定义函数 f(x)f(x)

f(x)={x,x02n+x,x<0f(x)= \begin{cases} x, & x \ge 0 \\ 2^n+x, & x < 0 \end{cases}

对于整数 xxf(x)f(x) 就是 xx 的补码对应的无符号整数值;换言之,把 f(x)f(x) 对应的值转换为二进制表示,就是 xx 的补码表示。

这里 xx 的取值范围是 [2n1,2n11][-2^{n-1}, 2^{n-1}-1],这也是用 nn 位二进制数表示有符号整数的取值范围。

你可以用十进制来帮助理解

对于十进制数字,nn 位数字能表示的范围是 [0,10n1][0, 10^n-1]。这里共有 10n10^n 种可能的组合,这是因为每一位有 1010 种可能的值,nn 位数字就有 10n10^n 种可能的组合。

对于二进制也是同理,但是由于我们第一位表示了符号位,所以正、负数字表示的范围都被砍半了。

补码 (1000...0) 的特殊性

你会发现,补码 (1000...0)2(1000...0)_2 对应的有符号整数值是 2n1-2^{n-1},而这个值用同样位数的原码是无法表示出来的。按照原码的定义,(0000...0)2(0000...0)_2(1000...0)2(1000...0)_2 都是 00,浪费掉了一个编码。

由于原码无法表示 2n1-2^{n-1},我们在目前这里直接断定 f(x)f(x) 的定义域包含 2n1-2^{n-1} 也并不严谨。

上面这些是大多数课程在初识补码的时候会介绍的转换方法,而补码背后的实际原理是下文将会介绍的同余。使用补码 (1000...0)2(1000...0)_2 来表示 2n1-2^{n-1} 符合同余相关的特性,也可以正确进行运算,因而直接把 2n1-2^{n-1} 包含在 f(x)f(x) 的定义域中是正确的。

使用补码做加法

使用原码的情况下,正整数直接相加的结果是正确的。例如,3+23 + 2 的二进制原码表示是 (0011)2+(0010)2=(0101)2(0011)_2 + (0010)_2 = (0101)_2,而 (0101)2(0101)_2 对应的有符号整数值正是 55

然而,我们想进行包含负数的加法是困难的。例如,1+(1)1 + (-1) 的二进制原码表示是 (0001)2+(1001)2=(1010)2(0001)_2 + (1001)_2 = (1010)_2,而 (1010)2(1010)_2 作为原码对应的有符号整数值是 2-2,显然不正确。

为了简化计算机硬件,我们希望数字的运算可以统一,而不是为负数专门发明一种新的运算方法。而补码正可以解决这个问题。

我们接下来证明:在用补码表示的情况下,对于有符号整数 xxyy,直接对二者的补码进行朴素的二进制相加,得到的结果就是 x+yx+y 的补码表示。此处我们讨论的是 nn 位二进制数的情况,对于运算过程中的溢出情况不做考虑。

xxyy 都是正数时,补码就是原码本身,相加的正确性是显然的。

当其中有一个是负数时,假设 xx 是正数,yy 是负数。根据上面的推导可得,yy 的补码对应的无符号整数值是 2n+y2^n+y。那么,直接将 xxyy 的补码视作无符号整数进行相加的结果就是:

Ans=x+(2n+y)=2n+(x+y)Ans = x + (2^n + y) = 2^n + (x + y)

nn 位无符号整数的取值范围是 [0,2n1][0, 2^n-1],而 2n+(x+y)2^n + (x + y) 对应的无符号整数值很可能超过这个范围。但这也无关紧要,因为 2n2^n 在二进制中是一个最高位为 11,其余位为 00n+1n+1 位数,而这个数表示为 nn 位二进制数的时候会溢出,最终表示就是 (0000...0)2(0000...0)_2

所以,在上式中,2n2^n 存在的作用是,如果 x+yx+y 的结果是负数,那么 2n2^n 的存在会让最终的结果回到 [0,2n1][0, 2^n-1] 的范围内;而如果 x+yx+y 的结果是正数,那么 2n2^n 的存在不会影响最终的结果。

换言之,AnsAns 的值实际上就是 x+yx+y2n2^n 取模后的结果,即 Ansx+y(mod2n)Ans \equiv x+y \pmod{2^n}。写到这里你还会发现,实际上 xxyy 有一个还是两个负数都是一样的,结果都是 x+yx+y2n2^n 取模后的结果。

我们再把上面的 f(x)f(x) 拿下来,但是这次加入取值范围:

f(x)={x,0x2n112n+x,2n1x<0,f(x)[0,2n1]f(x)= \begin{cases} x, & 0 \le x \le 2^{n-1}-1 \\ 2^n+x, & -2^{n-1} \le x < 0 \end{cases}, f(x) \in [0, 2^n-1]

那么我们也不难据此写出 f1(x)f^{-1}(x)

f1(x)={x,0x2n11x2n,2n1x2n1,f1(x)[2n1,2n11]f^{-1}(x)= \begin{cases} x, & 0 \le x \le 2^{n-1}-1 \\ x-2^n, & 2^{n-1} \le x \le 2^n-1 \end{cases}, f^{-1}(x) \in [-2^{n-1}, 2^{n-1}-1]

这样我们就可以用 f1(Ans)f^{-1}(Ans) 来得到 x+yx+y 的真实结果。

至此我们证明了:在用补码表示的情况下,对于有符号整数 xxyy,直接对二者补码进行朴素的二进制相加,得到的结果就是 x+yx+y 的补码表示

进一步观察新的 f(x)f(x),我们不难看出,f(x)f(x) 实际上就是一个通过对 2n2^n 取模,把 [2n1,2n11][-2^{n-1}, 2^{n-1}-1] 映射到 [0,2n1][0, 2^n-1] 的函数

加减法的统一

对于aba-b,计算机内没有单独的减法器元件,而是改为 a+(b+1)a+(\mathord{\sim}b+1) 进行计算。

此处 b\mathord{\sim}b 表示对 bb补码按位取反操作。

让我们来证明这一转换的正确性。

我们令 b=f(b)b' = f(b),即 bb'bb 的补码对应的无符号整数值。

我们根据先前推导补码的经验不难得出:

b+1=((2n1)b)+1=2nb\mathord{\sim}b + 1 = ((2^n - 1) - b') + 1 = 2^n - b'

这里无论 bb' 的值是 bb 还是 2n+b2^n + b,都有:

(b+1)bb(mod2n)(\mathord{\sim}b + 1) \equiv -b' \equiv -b \pmod{2^n}

进而有:

a+(b+1)abab(mod2n)a + (\mathord{\sim}b + 1) \equiv a - b' \equiv a - b \pmod{2^n}

而同余 2n2^n 意味着在 nn 位二进制表示下,这三者的数值相等。

至此,减法可以通过按位取反操作,转换为加法运算来实现。

致谢

感谢 KazimierzY 在推导过程中提供的帮助。

参考资料:

Ukraine 在俄罗斯对乌克兰发动的野蛮的侵略战争中矢志不渝地支持乌克兰