Skip to content

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

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

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

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

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

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

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

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

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

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

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

[X]={X0X<2n12n1+|X|2n1<X0

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

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

[X]={X0X<2n12n+X2n1X<0

范围 2n1+(2n11),比原码/反码多一个最小负数

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

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

[X]=2n1+X(2n1X<2n1)

范围与补码相同。

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

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

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

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

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

XYX+(2nY)(mod2n)

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

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

281101100=10000000产生符号位+(11111111101100)数值位按位取反+1末位加 1

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

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

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

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

  • 补码定义式 2n+X2845=211=(11010011)2
  • 移码定义式 2n1+X27+(45)=12845=83=(01010011)2

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

二、四位字长的全表

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

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

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

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

X=i=0n1bi2i,0X2n1

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

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

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

🔴 零的表示:原码 / 反码各两个,补码 / 移码唯一。 [0]=100[0]=111

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

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

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

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

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

X=bn12n1+i=0n2bi2i

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

位串展开真值
10000000128+0128
10000001128+1127
11111111128+1271

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

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

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

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

X=2n1+(低位值)2n=2n1+(低位值)

正是上式。bn1=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. 四种编码是对"符号位能否参加运算"的四种回答,差别只在负数怎么编;零的表示是原码/反码各两个、补码/移码唯一,补码多出的 2n1 正是从 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
  • 2n1n 位与 n+1 位下机器数不同、给定编码必须明确位数:同书 §2.1.4 例 2.12,印刷页 p30

相关知识

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

真题练习