2.3 无符号数、有符号数与补码
第 2.2 节完成了不同底数之间的数值转换。但存入机器的比特模式不会自带“正数”或“负数”标签,硬件与软件必须共同约定一种整数编码(integer encoding)。
观察 8 位模式 11111111。它可以表示:
- 无符号整数 255;
- 符号-数值编码中的 −127;
- 反码中的负零;
- 补码整数 −1。
比特完全相同,改变的只是解释约定。
无符号整数把每一位都当作正位权
对于 位无符号整数:
取值范围是:
因此,8 位无符号整数的范围是 0 到 255,最高位具有正权重 。
字节计数、存储地址偏移等不会为负的量适合使用无符号编码,但仍要确认最大值足够。
三种表示正负号的方法
符号-数值编码
最高位表示符号:0 表示正,1 表示负;其余位保存绝对值。
在 8 位格式中:
00000101表示 +5;
10000101表示 −5;
00000000与10000000都表示零。
两种零模式会增加比较与算术的复杂度。
反码
负数由对应正数模式逐位取反得到。
| 反码数值 | 8 位模式 |
|---|---|
00000101 | |
11111010 |
反码同样有两个零:全 0 表示正零,全 1 表示负零。
补码
负数由正数绝对值逐位取反后,在固定宽度内加 1 得到。
以 8 位 +5 为例:
所以 11111011 表示 −5。
补码只有一个零,而且同一套二进制加法器就能处理正负加法。现代处理器普遍采用补码表示有符号整数。
实验 1——保持比特不变,只改变解释约定
构造三个重要的 8 位模式,比较它们在无符号、符号-数值、反码与补码规则下的数值。
请特别观察全 1 模式和只有最高位为 1 的模式。这些边界模式能揭示不同编码的范围与零规则。
直接读取补码模式
对于 位补码,最高位权重为 ,其余低位仍保持正权重:
解码 11111011:
还有一个等价的快捷方法:
1. 先把整个模式解释为无符号数;
2. 如果最高位为 1,就减去 。
11111011 的无符号值是 251,所以 。
不对称的取值范围
位补码整数的范围是:
8 位范围为 −128 到 +127。负数比正数多一个,是因为零占用了一个非负模式。
| 模式 | 8 位补码值 |
|---|---|
00000000 | 0 |
00000001 | 1 |
01111111 | 127 |
10000000 | −128 |
11111111 | −1 |
−128 没有可用 8 位表示的正数对应值。在八位范围内对它取负仍会得到相同模式,因为 +128 已经越界。编写算术程序时必须注意这个边界。
实验 2——锻造负数补码
从 8 位绝对值出发,执行取反、加 1 或手工切换任意位,直到工作模式解码为目标负数。
完成普通数值后,再尝试 −1 与 −128,并解释为什么它们最终分别得到全 1 和 10000000。
改变宽度需要符号扩展
增加无符号数宽度时,在左侧补零:
增加补码数宽度时,需要在左侧复制符号位,这叫作符号扩展(sign extension):
如果在负补码左侧补零,它的符号与数值都会改变。
缩短宽度只有在被删除的高位都是冗余符号扩展位时才安全。截断前必须先解码或检查范围。
检查点
请始终把“存储模式”和“解释规则”分开:
- 无符号编码只使用正的二次幂;
- 符号-数值编码分别保存符号与绝对值;
- 反码通过逐位取反形成负数,并有两个零;
- 补码通过取反加一形成负数,只有一个零,范围为 到 。
下一节将研究算术结果无法装入固定宽度时会发生什么,并比较二进制整数、BCD 与字符编码。