Skip to content

真值与机器数:四种编码的定义 ​

2026 大纲 二(一)2 定点数的编码表示 · 二(三)整数的表示和运算。二(三)的无符号整数与带符号整数,正是本篇中"纯二进制"与"补码"两种编码在整数上的落地。

机器里没有"负号"这个符号 ​

真值是带正负号的实际数值,是人写的形式;机器数是连符号一起编码成 0/1 位串的形式。机器里根本没有"−"这个符号可用,正负必须编码进位串本身。

于是问题变成:符号编进去之后,怎么让它别碍事?四种编码回答的都是同一个问题——

🔴 符号位数字化之后,能不能和数值部分一起参加运算? 不能,就得先取符号、比绝对值、再决定做加还是做减;能,加法器直接把 n 位一起加就完了。四种编码的差别只在负数怎么编——正数在原码、反码、补码三者中完全相同。

补码之所以成为整数的通用表示,理由只有一条:

🔴 它能用加法做减法,根据是模运算。 n 位寄存器丢弃进位等价于对 2n 取模,于是 X−Y≡X+(2n−Y)(mod2n),而 2n−Y 正是 [−Y]补。顺带一提,"取反加 1"不是口诀,它是定义式 2n+X 的代数展开(见下方折叠块)。

⚠️ 读定义式前先确认一件事:本篇的 n 指机器字长(含 1 位符号位),数值位是 n−1 位。有些教材用 n 表示数值位数,定义式写成 [X]补=2n+1+X。两套约定都对,但代进表示范围会差一位——看别的书时先确认它的 n 是哪个。

一、四种编码的定义式与表示范围 ​

原码:符号位取真值的正负(正 0 负 1),数值位直接写绝对值。

[X]原={X0≤X<2n−12n−1+|X|−2n−1<X≤0

式中 2n−1+|X| 就是"把符号位置 1、数值位保持绝对值"。范围 −(2n−1−1)∼+(2n−1−1)。原码直观且乘除方便(绝对值相乘、符号位单独异或),坏处是加减法必须先比较绝对值再决定做加还是做减——这个代价就是补码出现的动机。

补码:正数与原码相同;负数是反码加 1。

[X]补={X0≤X<2n−12n+X−2n−1≤X<0

范围 −2n−1∼+(2n−1−1),比原码/反码多一个最小负数。

反码:正数与原码相同;负数是原码的数值位按位取反(符号位不变)。范围与原码相同。它现在几乎不单独使用——从"取反加 1"的推导可以看出它的真实身份:它就是 2n−1−|X| 那一步的中间结果,链条"原码 → 反码 → 补码"里的第二环。历史上曾有机器直接用反码做运算,但双零和"循环进位"的麻烦让它被补码取代。

移码:在真值上加一个偏置常数(这里取 2n−1)。

[X]移=2n−1+X(−2n−1≤X<2n−1)

范围与补码相同。

⚠️ IEEE 754 的阶码也是移码,只是偏置常数不取 2n−1 而取 2n−1−1(单精度 127、双精度 1023)。别把"移码的最小真值对应全 0"与"IEEE 754 里阶码全 0 有特殊含义"混为一谈——后者是标准对特殊值的编码约定,与移码本身的性质无关。

⚠️ 还有一条前提要说死:只给一串位是确定不了真值的,还必须知道用的是哪种编码和字长是多少。同一个真值在不同位数下机器数不同(−2n−1 在 n 位下是 10...0、在 n+1 位下是 110...0),同一串位在不同编码下真值也不同。

补码为什么能把减法变成加法,以及"取反加 1"的代数展开(想弄清这两条是推出来的而不是硬记的时展开)

模运算。 钟表是个模 12 的系统:时针从 10 点倒拨 4 格,可以逆时针拨 4 格(−4),也可以顺时针拨 8 格(+8)——因为 −4≡8(mod12),两者结果都是 6 点。

n 位寄存器就是一个模 2n 的系统:超出 n 位的进位自动丢弃,等价于对 2n 取模。所以

X−Y≡X+(2n−Y)(mod2n)

只要用 2n−Y 这个"补数"代替 −Y,减法就变成了加法。而 2n−Y 正是 [−Y]补 的定义式。于是"符号位能不能一起算"这个问题得到了肯定回答:补码的符号位可以和数值位一起参加加法运算,加法器不必知道操作数的正负。

"取反加 1"的来历。 定义式里是 2n+X(X 为负),实际操作却是"取反加 1"。以 8 位、|X|=1101100 为例:

28−1101100=10000000⏟产生符号位+(1111111−1101100)⏟数值位按位取反+1⏟末位加 1

拆项之后一目了然:28=10000000+10000000,前一个 10000000 留下来充当符号位 1,后一个拆成 1111111+1;而 1111111− 数值位就是逐位取反(每一位不是 0 就是 1,用 1 减等于翻转),剩下那个 1 就是末位加 1。

用一个数把"定义式"和"取反加 1 操作"对上号。 取 8 位、真值 −45,45=(101101)2,7 位数值位写作 0101101:

编码结果怎么来的(操作路径)
原码10101101符号位 1 + 绝对值
反码11010010原码数值位取反
补码11010011反码加 1
移码01010011补码符号位取反

再走定义式这条路,两边必须落到同一串位:

  • 补码定义式 2n+X:28−45=211=(11010011)2 ✓
  • 移码定义式 2n−1+X:27+(−45)=128−45=83=(01010011)2 ✓

操作路径与定义式给出同一个结果——这正是上面那段代数展开所证明的事。手算时用操作路径快,判断边界或位数变化时回到定义式才靠得住。

二、四位字长的全表 ​

真值原码反码补码移码
+70111011101111111
+60110011001101110
+50101010101011101
+40100010001001100
+30011001100111011
+20010001000101010
+10001000100011001
+00000000000001000
−010001111——
−11001111011110111
−21010110111100110
−31011110011010101
−41100101111000100
−51101101010110011
−61110100110100010
−71111100010010001
−8——10000000

看最后两行:−8 只有补码和移码能表示,原码/反码的那个位串被 −0 占用了。再看移码那一列从上到下严格递减、从下到上严格递增——这正是它能直接按无符号比大小的直观形态。

三、还有一种"第零号编码":无符号数 ​

上面四种回答的都是"符号怎么编"。如果一个量根本不会为负(地址、计数值、位串本身),就没必要留出符号位——n 位全部当数值位:

X=∑i=0n−1bi⋅2i,0≤X≤2n−1

它与补码的关系就是最高位权的差别:补码把最高位的权取成 −2n−1,无符号取成 +2n−1,两者相差 2n。所以 8 位的 11111111 按补码是 −1、按无符号是 255。它没有符号位,也就谈不上"看两个符号位判溢出"那一套——无符号加法的溢出只看最高位是否产生进位,见 补码加减法与溢出判断。

编码负数怎么编零表示范围(n 位)主要用途
无符号不表示负数唯一[0, 2n−1]地址、计数、位串
原码符号位 1 + 绝对值两个[−(2n−1−1), 2n−1−1]乘除法的数值部分、浮点尾数
反码原码数值位取反两个[−(2n−1−1), 2n−1−1]求补码的中间步骤
补码反码 + 1唯一[−2n−1, 2n−1−1]整数的通用表示、加减运算
移码补码符号位取反唯一[−2n−1, 2n−1−1]浮点数的阶码

这张表里有三处不是巧合:

🔴 零的表示:原码 / 反码各两个,补码 / 移码唯一。 [−0]原=10…0,[−0]反=11…1。

🔴 补码为什么比原码、反码多一个数:原码与反码各浪费了一个位串在 −0 上。n 位一共 2n 个位串是固定的,少一个零就能多一个数,空出来的那个给了 −2n−1。所以范围的不对称不是设计缺陷,是消除冗余的必然结果。左右不对称的算法也随之清楚:正数 2n−1−1 个、负数 2n−1 个、零 1 个,加起来正好 2n——零被算进了非负那一侧,所以负数多一个。

🔴 移码 = 补码的符号位取反,这个改动的用意是:加偏置之后最小真值映射到全 0、最大真值映射到全 1,中间严格单调递增,于是"真值的大小顺序"与"把位串当无符号整数读的顺序"完全一致——浮点比大小时硬件直接按位串比就行。补码做不到这一点:补码里 −1 是全 1,按无符号读反而是最大值。

一句话选型:要按位串直接比大小就用移码,要让符号位参与加法就用补码。 阶码需要前者,整数需要后者。

四、按权展开:把符号位当成一个负权 ​

前面的分段定义适合"给真值求编码"。反过来"给一串位求真值",用按权展开更快——只要把最高位的权取成负的:

X=−bn−1⋅2n−1+∑i=0n−2bi⋅2i

8 位的具体形态是 X=−128b7+64b6+32b5+16b4+8b3+4b2+2b1+b0:

位串展开真值
10000000−128+0−128
10000001−128+1−127
11111111−128+127−1

🔴 由位串求真值,走按权展开最快。 口诀是:符号位为 1 时,真值 =−2n−1 再加上低 n−1 位当无符号数读出的值。位串里 1 越多,这条路越省事。

🔴 另外澄清一个常见误解:10000000 不是溢出。 8 位补码的范围是 [−128,+127],−128 是合法值。"求补必溢出"说的是对它做求补运算会溢出(+128 超出上界),不是它本身非法。

这个式子怎么来的,以及用它构造"含 k 个 1 的最小补码"(碰到给 1 的个数求极值这类题、或想确认它对正数也成立时展开)

设最高位 bn−1=1,则位串的无符号值为 2n−1+(低 n−1 位的值)。而补码定义说这串位代表 无符号值−2n,于是

X=2n−1+(低位值)−2n=−2n−1+(低位值)

正是上式。bn−1=0 时它退化为普通的无符号展开,同样成立。

用它构造极值。 问:由 3 个 1 和 5 个 0 组成的 8 位二进制补码,能表示的最小整数是多少?

  1. 定符号位。要最小(最负),符号位必须是 1——符号位为 0 的都是非负数,一定比它大。用掉一个 1。
  2. 写出展开式。符号位为 1 ⇒ 真值 =−128+(低 7 位的无符号值)。
  3. 最小化剩下的部分。低 7 位还剩 2 个 1 和 5 个 0,要让它作为无符号数尽可能小,就把两个 1 全塞到最低位:0000011 =3。
Xmin=−128+3=−125,机器数 10000011

第 3 步为什么不能随手放:满足"3 个 1"的位串不止一个,11100000 同样有 3 个 1,但两个 1 摆在高位,低 7 位读出来是 96,真值 =−128+96=−32——合法,却远不是最小。约束限定了 1 的个数,最小化则要求把这些 1 尽量往低位摆,两件事要分开想。

考点速记 ​

  1. 四种编码是对"符号位能否参加运算"的四种回答,差别只在负数怎么编;零的表示是原码/反码各两个、补码/移码唯一,补码多出的 −2n−1 正是从 −0 那里省下来的。
  2. 补码能用加法做减法的原因是模运算(n 位寄存器丢弃进位等价于对 2n 取模),"取反加 1"是定义式 2n+X 的代数展开,不是口诀。
  3. 移码 = 补码符号位取反,它把真值顺序映射成无符号数顺序,所以用于阶码;由位串求真值走按权展开(最高位取负权)最快;给定编码必须交代编码方式和位数,否则边界问题无解。

这一节在真题里被考过的形式(下方「真题练习」里属于本篇的那几道):

  • 给一个十进制真值,问它的机器数(十六进制):正数直接转;负数走 2n+X 或"取反加 1",算完用按权展开验一遍。⚠️ 位数按题面给的类型定(short 是 16 位),别按 32 位算。
  • 给机器数问真值,并接着做一次加减:先按权展开求出两个真值(最高位取负权最快),算完再编码回去。这类题通常在第二问上设溢出陷阱。
  • 给几个机器数,问按无符号 / 按带符号解释时的大小关系:同一串位在两种解释下的顺序可以完全不同。按无符号就直接比位串;按带符号先看最高位——负数一律小于非负数,都是负数时再比。这道题考的正是"补码顺序 ≠ 位串顺序"。
  • 给一个机器数,问它按 int 或按 float 解释时的值可能是多少:把同一串位分别按补码和 IEEE 754 各读一遍,看哪个选项对得上。这是"位串本身没有类型"的最直接考法。
  • 问由若干个 1 和若干个 0 组成的 n 位补码能表示的最小整数:要最小就要最负,所以符号位必须是 1,剩下的 1 尽量往低位放(数值部分越小越负)。

易错:把补码的表示范围写成左右对称。负数比正数多一个,因为零算在非负那侧。

易错:按无符号的顺序去比补码的大小。补码里全 1 是 −1 而不是最大值。

易错:位数按 32 位默认算。short 是 16 位,题面给什么类型就用什么位数。

易错:把"取反加 1"当成孤立口诀。它就是 2n+X,拿不准时回到定义式。

教材出处
  • 四种编码的定义式与表示范围:唐朔飞《计算机组成原理》第 3 版 §6.1 无符号数和有符号数,印刷页 p223–p228(含表 6.1 全位串对照表、图 6.1 移码数轴)
  • 模运算动机(钟表模 12 的例子)与"取反加 1"的代数展开:袁春风《计算机组成与系统结构》第 3 版 §2.1.4 定点数的编码,印刷页 p29–p31
  • −2n−1 在 n 位与 n+1 位下机器数不同、给定编码必须明确位数:同书 §2.1.4 例 2.12,印刷页 p30

相关知识 ​

定点数编码的转换与书写|补码加减运算与溢出判别|浮点数表示(IEEE 754)

真题练习 ​