如何证明使用补码时,先取反再加一,就能得到对应的相反数?

58 阅读2分钟

背景

对使用 NN 位补码表示的 xx (xx 是满足 2N1x2N11-2^{N-1}\le x\le 2^{N-1}-1 的整数)而言,如果依次进行如下两步操作

  1. y=xy=\sim{x} (这里的 \sim 表示按位取反)
  2. z=y+1z=y+1 (这里的 ++NN 位无符号数的加法)

那么得到的 zz 满足

  • z=xz=-x (如果x2N1x\ne -2^{N-1})
  • z=xz=x (如果 x=2N1x=-2^{N-1})

本文会对此进行证明。

正文

补码和模运算

运用 NN 运算(NN 是正整数),我们可以将所有整数划分成 NN 个等价类。以 N=16N=16 为例,我们可以按照 xmod16x \bmod 16 的结果把整数划分为如下 1616 个等价类。

  • 等价类 00: 如果 xmod16=0x \bmod 16 = 0,则 xx 属于这个等价类
  • 等价类 11: 如果 xmod16=1x \bmod 16 = 1,则 xx 属于这个等价类
  • 等价类 22: 如果 xmod16=2x \bmod 16 = 2,则 xx 属于这个等价类
  • 等价类 33: 如果 xmod16=3x \bmod 16 = 3,则 xx 属于这个等价类
  • 等价类 44: 如果 xmod16=4x \bmod 16 = 4,则 xx 属于这个等价类
  • 等价类 55: 如果 xmod16=5x \bmod 16 = 5,则 xx 属于这个等价类
  • 等价类 66: 如果 xmod16=6x \bmod 16 = 6,则 xx 属于这个等价类
  • 等价类 77: 如果 xmod16=7x \bmod 16 = 7,则 xx 属于这个等价类
  • 等价类 88: 如果 xmod16=8x \bmod 16 = 8,则 xx 属于这个等价类
  • 等价类 99: 如果 xmod16=9x \bmod 16 = 9,则 xx 属于这个等价类
  • 等价类 1010: 如果 xmod16=10x \bmod 16 = 10,则 xx 属于这个等价类
  • 等价类 1111: 如果 xmod16=11x \bmod 16 = 11,则 xx 属于这个等价类
  • 等价类 1212: 如果 xmod16=12x \bmod 16 = 12,则 xx 属于这个等价类
  • 等价类 1313: 如果 xmod16=13x \bmod 16 = 13,则 xx 属于这个等价类
  • 等价类 1414: 如果 xmod16=14x \bmod 16 = 14,则 xx 属于这个等价类
  • 等价类 1515: 如果 xmod16=15x \bmod 16 = 15,则 xx 属于这个等价类

我在下图中将这 1616 个等价类用 22 进制表示。

pie.png

我们可以从这 1616 个等价类中分别选择一个元素作为代表。一个简单的做法是,选择这个等价类中绝对值最小的非负数,即 0,1,2,3,,150,1,2,3,\cdots,15 作为代表。为了便于叙述,我们将这种做法称为 Solution1Solution_1。而补码运算的选择与之不同,我们把补码运算从等价类中选择数字的方式称为 Solution2Solution_2Solution1Solution_1Solution2Solution_2 的对比如下表所示 ⬇️

1616 运算中的等价类Solution1Solution_1Solution2Solution_2 (补码运算的选择)Solution1Solution_1Solution2Solution_2 的选择相同吗?
000000000000
000100011111
001000102222
001100113333
010001004444
010101015555
011001106666
011101117777
10001000888-8
10011001997-7
1010101010106-6
1011101111115-5
1100110012124-4
1101110113133-3
1110111014142-2
1111111115151-1

所以,对一个 NN22 进制数 xx 而言,

  • 当它的最高位(即 MSB, Most Significant Bit)是 11
    • 如果我们分别把 xx 看成 无符号数 xux_u补码 xcx_c,那么 xc=xu2Nx_c=x_u-2^N
  • 当它的最高位是 00
    • 如果我们分别把 xx 看成 无符号数 xux_u补码 xcx_c,那么 xc=xux_c=x_u

本文在举例时,均以 N=4N=4 来进行说明。我们以 N=4N=4 为例,看一看将各个 22 进制数视为 无符号数补码 时的值是怎样的 ⬇️

4422 进制数 xx将其视为无符号数时的值 xux_u将其视为补码时的值 xcx_cxuxcx_u \to x_c 的计算过程
(xx 最高位为 00 时,
xu=xcx_u=x_c 总成立,
转化过程略)
000000000000
000100011111
001000102222
001100113333
010001004444
010101015555
011001106666
011101117777
10001000888-8816=88-16=-8
10011001997-7916=79-16=-7
1010101010106-61016=610-16=-6
1011101111115-51116=511-16=-5
1100110012124-41216=412-16=-4
1101110113133-31316=313-16=-3
1110111014142-21416=214-16=-2
1111111115151-11516=115-16=-1

证明

我们可以把 xx 的值分为 22 类 ⬇️

  • 非负数: 0x2N110\le x\le 2^{N-1}-1
  • 负数: 2N1x1-2^{N-1}\le x\le -1

情形 11: 当 0x<2N10\le x\lt 2^{N-1}

因为 x=0x=0 的情况比较特殊,我们把它单独列出来,于是 0x<2N10\le x\lt 2^{N-1} 可以细分为两种情形

  • x=0x=0
  • 1x2N111\le x\le 2^{N-1} - 1
情形 1.11.1x=0x=0
步骤写成 NN22 进制数22 进制数视为无符号数时的值 xu,yu,zux_u,y_u,z_u22 进制数视为补码时的值 xc,yc,zcx_c,y_c,z_cxuxcx_u\to x_c
yuycy_u\to y_c
zuzcz_u\to z_c
是如何计算的
初始值 xxx:000n timesN bitx:\underbrace{00\cdots 0}_{n\rm\ times}^{\text{N bit}}xu=0x_u=0xc=0x_c=0xc=xu=0x_c=x_u=0
取反 得到 yyy:111n timesN bity:\underbrace{11\cdots 1}_{n\rm\ times}^{\text{N bit}}yu=2N1y_u=2^N-1yc=1y_c=-1(2N1)2N=1(2^N-1)-2^N=-1
加一 得到 zzz:000n timesN bitz:\underbrace{00\cdots 0}_{n\rm\ times}^{\text{N bit}}zu=0z_u=0zc=0z_c=0zc=zu=0z_c=z_u=0

N=4N=4 为例,取反和加一的过程如下图所示

mermaid-diagram-2025-12-20-195415.png

情形 1.21.21x2N111\le x\le 2^{N-1}-1
y=x=(2N1)xy=\sim x=(2^N-1)-x

因为

1x2N111\le x\le 2^{N-1}-1

所以

2N1y2N22^{N-1}\le y\le 2^N-2

所以 yy 对应的 NN22 进制数的最高位是 11 ⬇️ (\cdots 表示这里不关心那些位的值)

y=1n timesN-1 bity=1\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}

z=y+1z=y+1,所以

2N1+1z<2N12^{N-1} + 1\le z\lt 2^N-1

所以 zz 对应 NN22 进制数的最高位是 11 ⬇️ (\cdots 表示这里不关心那些位的值)

z=1n timesN-1 bitz=1\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}

zz 对应的补码 zcz_c 所表示的值满足

zcz_c
=z2N=z-2^N
=y+12N=y+1-2^N
=(2N1x)+12N=(2^N-1-x)+1-2^N
=x=-x

1x2N111\le x\le 2^{N-1}-1 时,计算步骤可以汇总成如下的表格 ⬇️

步骤写成 NN22 进制数22 进制数视为无符号数时的值 xu,yu,zux_u,y_u,z_u22 进制数视为补码时的值 xc,yc,zcx_c,y_c,z_cxuxcx_u\to x_c
yuycy_u\to y_c
zuzcz_u\to z_c
是如何计算的
初始值 xxx:0n timesN-1 bitx:0\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}xu=xx_u=xxc=xx_c=xxc=xu=xx_c=x_u=x
取反 得到 yyy:1n timesN-1 bity:1\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}yu=(2N1)xy_u=(2^N-1)-xyc=1xy_c=-1-x(2N1)x2N=1x(2^N-1)-x-2^N=-1-x
加一 得到 zzz:1n timesN-1 bitz:1\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}zu=2Nxz_u=2^N-xzc=xz_c=-x2Nx2N=x2^N-x-2^N=-x

N=4,x=5N=4,x=5 为例,取反和加一的过程如下图所示

mermaid-diagram-2025-12-20-201307.png

情形 22: 当 2N1x1-2^{N-1}\le x\le -1

因为 x=2N1x=-2^{N-1} 的情况比较特殊,我们把它单独列出来,于是 2N1x1-2^{N-1}\le x\le -1 可以细分为两种情形

  • x=2N1x=-2^{N-1}
  • 2N1<x1-2^{N-1}\lt x\le - 1
情形 2.12.1x=2N1x=-2^{N-1}
步骤写成 NN22 进制数22 进制数视为无符号数时的值 xu,yu,zux_u,y_u,z_u22 进制数视为补码时的值 xc,yc,zcx_c,y_c,z_cxuxcx_u\to x_c
yuycy_u\to y_c
zuzcz_u\to z_c
是如何计算的
初始值 xxx:100n timesN-1 bitx:1\underbrace{0\cdots 0}_{n\rm\ times}^{\text{N-1 bit}}xu=2N1x_u=2^{N-1}xc=2N1x_c=-2^{N-1}2N12N=2N12^{N-1}-2^N=-2^{N-1}
取反 得到 yyy:011n timesN-1 bity:0\underbrace{1\cdots 1}_{n\rm\ times}^{\text{N-1 bit}}yu=2N11y_u=2^{N-1}-1yc=2N11y_c=2^{N-1}-1yc=yu=yy_c=y_u=y
加一 得到 zzz:100n timesN-1 bitz:1\underbrace{0\cdots 0}_{n\rm\ times}^{\text{N-1 bit}}zu=2N1z_u=2^{N-1}zc=2N1z_c=-2^{N-1}2N12N=2N12^{N-1}-2^N=-2^{N-1}

N=4N=4 为例,取反和加一的过程如下图所示

mermaid-diagram-2025-12-20-201557.png

情形 2.22.22N1<x1-2^{N-1}\lt x\le - 1
y=x=(2N1)(x+2N)=x1y=\sim x=(2^N-1)-(x+2^N)=-x-1

因为

2N1<x1-2^{N-1}\lt x\le - 1

所以

0y<2N110\le y\lt 2^{N-1}-1

所以 yy 对应的 NN22 进制数的最高位是 00 ⬇️ (\cdots 表示这里不关心那些位的值)

y=0n timesN-1 bity=0\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}

z=y+1z=y+1,所以

1z<2N11\le z\lt 2^{N-1}

所以 zz 对应 NN22 进制数的最高位是 11 ⬇️ (\cdots 表示这里不关心那些位的值)

z=0n timesN-1 bitz=0\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}

zz 对应的补码 zcz_c 所表示的值满足

zcz_c
=z=z
=y+1=y+1
=(x1)+1=(-x-1)+1
=x=-x

2N1<x1-2^{N-1}\lt x\le - 1 时,计算步骤可以汇总成如下的表格 ⬇️

1x2N111\le x\le 2^{N-1}-1 时,计算步骤可以汇总成如下的表格 ⬇️

步骤写成 NN22 进制数22 进制数视为无符号数时的值 xu,yu,zux_u,y_u,z_u22 进制数视为补码时的值 xc,yc,zcx_c,y_c,z_cxuxcx_u\to x_c
yuycy_u\to y_c
zuzcz_u\to z_c
是如何计算的
初始值 xxx:1n timesN-1 bitx:1\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}xu=x+2Nx_u=x+2^Nxc=xx_c=x(x+2N)2N=x(x+2^N)-2^N=x
取反 变为 yyy:0n timesN-1 bity:0\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}yu=x1-y_u=x-1yc=x1y_c=-x-1yc=yu=yy_c=y_u=y
加一 变为 zzz:0n timesN-1 bitz:0\underbrace{\cdots}_{n\rm\ times}^{\text{N-1 bit}}zu=2Nxz_u=2^N-xzc=xz_c=-x2Nx2N=x2^N-x-2^N=-x

N=4,x=3N=4,x=-3 为例,取反和加一的过程如下图所示

mermaid-diagram-2025-12-20-201901.png

其他

文中展示取反和加一的过程的图是如何画出来的。我是 mermaid.live/ 上画图的。其中一张图的代码如下 ⬇️ (其他图的代码也是类似的,就不赘述了)

block
    block
        columns 5
        header0["步骤 ⬇️"] name1["carry"] name0["bits"] u["当成无符号数"] c["当成补码"]

        header1["(初始)"] carry0["0"] value0["1101"] u0["13"] c0["-3"]
        header2["取反"] carry1["0"] value1["0010"] u1["2"] c1["2"]
        header3["加一"] carry2["0"] value2["0011"] u2["3"] c2["3"]
    end

style header0 fill:#080;
style header1 fill:#0f0;
style header2 fill:#0f0;
style header3 fill:#0f0;

style value0 fill:#ff0;
style carry0 fill:#0ff;

style value1 fill:#ff0;
style carry1 fill:#0ff;

style value2 fill:#ff0;
style carry2 fill:#0ff;

style name0 fill:#d3d3d3,stroke:#fff;
style name1 fill:#088,stroke:#fff;


style u fill:#f80;
style c fill:#f80;
style u0 fill:#f80;
style c0 fill:#f80;
style u1 fill:#f80;
style c1 fill:#f80;
style u2 fill:#f80;
style c2 fill:#f80;

image.png

参考资料