十六进制是二进制的"官方缩写"——4 位并 1 位,两位刚好一个字节,所以颜色、地址全用它。负数没有"负号"可存——工程师试了原码(错)、反码(半对),最后用补码:在 256 格的时钟上,倒拨 5 格和正拨 251 格落在同一点,所以 −1 就是 255、−5 就是 251。03 篇"扔掉溢出位"的秘密,全在这口钟上。
1. 开场:两个你迟早会撞上的谜题
- 一串 8 位二进制
11111111——是无符号的 255,还是有符号的 −1? - 颜色代码
#FF8800里的字母是什么意思?为什么 0~9 不够还要 A、B、C?
这篇一并解开。前半讲十六进制(快),后半讲负数(本篇核心)。
2. 十六进制:给人看的二进制缩写
二进制有个致命缺点:太长。写个 255 要 8 位,写个内存地址要 32~64 位——人根本读不动。十六进制的解法很聪明:
2⁴ = 16,所以恰好每 4 位二进制并成 1 位十六进制;8 位(1 字节)就是 2 位。
十六进制需要 16 个符号:0~9 用完了,补上 A=10、B=11、C=12、D=13、E=14、F=15。
| 二进制 | 十六进制 | 十进制 |
|---|---|---|
0000 |
0 | 0 |
0101 |
5 | 5 |
1000 |
8 | 8 |
1010 |
A | 10 |
1111 |
F | 15 |
11111111 |
FF | 255 |
换算是"直通车",不用过十进制:FF8800 逐位拆 → 1111 1111 1000 1000 0000 0000。
- 颜色代码
#FF8800(网页、设计软件) - 内存地址
0x7FFF5FBFF8C0(0x前缀 = 十六进制) - MAC 地址
A4-5E-60-1F-2B-3C、UUID、Unicode 码点U+4E2D全是同一件事:把一长串 0/1 压短给人看。
3. #FF8800 解剖:三个字节 = 三盏灯
02 篇讲过:像素颜色 = 红绿蓝三通道,各 0~255(一个字节)。一个字节恰好两位十六进制,所以:
# FF 88 00
│ │ └─ 蓝 = 0 = 00000000(全关)
│ └──── 绿 = 0x88 = 136 = 10001000(过半)
└─────── 红 = 0xFF = 255 = 11111111(拉满)
→ 红加绿 = 橙色
为什么颜色天生适合十六进制?因为 6 个十六进制位 = 3 个字节 = 三通道,严丝合缝。同理,为什么满屏都是 255?——F = 15 = 1111,FF = 255 = 一个字节全开。
4. 负数怎么存:三次演变(本篇核心)
现在解第二个谜题。先明确约束:内存里只有 0 和 1,"负号"这个字符不存在——负号也必须用位来编码。历史上试了三版,值得从头走一遍,因为每一次"打补丁"都暴露一层认知:
第一版:原码——最高位当符号位
最自然的想法:最高位 0 表正、1 表负,其余 7 位照常。
+5 → 00000101 −5 → 10000101
直观,但一做加法就露馅:
5 + (−5):00000101 + 10000101 = 10001010(按位直接加)= −10?错得离谱。 更糟的是 0 有两个:00000000(+0)和10000000(−0)——电脑判断"等于零"要先处理两个零,处处别扭。
结论:符号位如果只是个"标记",就等于让加法器做加法时还得"看情况",电路无法统一。
第二版:反码——负数把正数按位取反
改进:负数 = 对应正数按位取反(0↔1)。
+5 → 00000101 −5 → 11111010
进步了:5 + (−5) = 00000101 + 11111010 = 11111111,加法方向对了。但两个零还在(00000000 和 11111111),加法在进位绕圈时还差 1。
第三版:补码——反码再加 1,问题根治
最终方案:负数 = 反码 + 1。
| 数 | 原码 | 反码 | 补码(实际采用) |
|---|---|---|---|
| +5 | 00000101 |
00000101 |
00000101 |
| −5 | 10000101 |
11111010 |
11111011 |
| −1 | 10000001 |
11111110 |
11111111 |
| +0 | 00000000 |
00000000 |
00000000 |
| −0 | 10000000 |
11111111 |
(+1 溢出 → 00000000,两个零合并成一个!) |
验证加法:5 + (−5) = 00000101 + 11111011 = 1 00000000——第 9 位溢出,扔掉(03 篇那一招),正好剩 00000000 = 0。✓
原码 → 反码 → 补码不是三套并列的理论,而是一条"发现问题 → 打补丁 → 根治"的路线:原码死在"符号位只是装饰、加法器没法统一处理";反码救了一半但两个零还在;补码加 1 这个动作顺手把 −0 推过一圈、和 +0 重合——零只有一个了,加法器从此不用看任何情况,一视同仁地加。
5. 补码为什么"合法":256 格的时钟
补码不只是"凑出来的技巧",它有坚实的数学身份——模运算。想象一口 8 位专用的钟:只有 256 个刻度(0~255),转满 256 回到原地。
在这口钟上:
- 倒拨 5 格(−5)和正拨 251 格(+251),落点是同一个——所以 −5 就"是" 251,写作
11111011 - −1 = 255(
11111111),−2 = 254……负数全住在钟的上半圈 5 + 251 = 256→ 转回一圈 → 0。"扔掉第 9 位"就是转回一圈重新对表——这就是 03 篇那一招为什么合法的全部原因- 顺手回答了 05 篇的伏笔:"补码"这个词也源于此——251 是 −5 在"模 256"意义下的补充数(相加恰好补满一圈 256)
256 个位组合要分给正负。补码规定上半圈(128~255)当负数、下半圈(0~127)当非负。这样 0 占一个位置后,正数(含 0)128 个、负数也是 128 个——但正数最大 127、负数最小 −128,负端比正端多 1,因为 0 这个"占位"算进了正侧。这是方案自洽的代价,不是 bug。
6. 回答开场谜题:255 还是 −1?——看上下文
现在两道题都有了答案:
11111111存储上永远是同一个字节。程序把它声明成"无符号数"就读 255,声明成"有符号数"就读 −1。位不决定含义,类型(读法)决定——这也解释了为什么有的程序 bug 出在"该用有符号的地方用了无符号"。#FF8800= 红 255、绿 136、蓝 0 = 橙色(02 篇的 RGB 加色模型 + 本篇十六进制)。
7. 常见误区
电脑内部只有二进制。十六进制是写给人看的缩写,跟中文备注一样,机器从不"使用"它。
[!warning] 误区二:负数 = 符号位 + 绝对值
那是原码——已被淘汰。真实的负数存的是补码(模意义下的"正拨量"),所以负数做加法不用任何特殊处理。
[!warning] 误区三:−1 的补码 11111111 是"设计得巧"
不是巧,是必然:钟上 −1 本来就该在"满一圈差一格"的位置。你推任何负数都会落在它该在的格子。
[!warning] 误区四:补码取负 = 改符号位
取负的正确操作:按位取反再 +1(或"从右往左找到第一个 1,左边全部取反")。直接改最高位得到的是原码,做加法会错。
[!warning] 误区五:8 位补码能表示 −128~+128
上限是 +127。−128~+127 共 256 个数,一个不多一个不少;"128 的负数"(−128 的相反数)在这套编码里存不下,硬存会绕圈出错。
8. 可折叠自测题
2⁴=16:4 位二进制并 1 位十六进制,2 位 = 1 字节,切分天然对齐。8 进制(3 位并 1)和字节(8 位)对不齐,所以字节时代十六进制完胜。
[!question]- Q2. 手算 #FF8800 的 RGB 三个值。
FF=255、88=8×16+8=136、00=0。红 255、绿 136、蓝 0 → 橙色。
[!question]- Q3. 原码和反码各死在什么问题上?
原码:符号位只是装饰,加法器没法统一处理,且 +0/−0 两个零。反码:加法方向对了,但两个零仍在、进位绕圈还差 1。补码加 1 后两零合一、加法完全统一。
[!question]- Q4. 用 8 位补码算 10 − 4,写出过程。
10 = 00001010;−4:4=00000100 → 反 11111011 → +1 = 11111100。
相加:00001010 + 11111100 = 1 00000110,扔掉第 9 位 → 00000110 = 6。✓(03 篇做过,现在你知道每一步为什么合法了)
[!question]- Q5. 11111111 到底是 255 还是 −1?10000000 呢?
都对——取决于程序把它声明成有符号还是无符号。11111111:无符号 255 / 有符号 −1;10000000:无符号 128 / 有符号 −128。存储相同,读法(类型)决定含义。
9. 下一步学什么
- [ ] 数据压缩原理 —— 哈夫曼、RLE,ZIP 到底做了什么
- [ ] 计算机五大部件 —— 冯·诺依曼结构里除 CPU 之外的其余四件
- [ ] 操作系统做什么 —— 存储程序之后的新问题:几十个程序怎么共处
- [ ] 浮点数 —— 本篇讲了整数怎么存,小数怎么存(为什么
0.1 + 0.2 ≠ 0.3)是下一个自然问题 - [ ] 写第一行真代码 —— 理论拼图已齐,随时可以动手,Python 装环境说一声就带
10. 相关笔记
- 03 用电表示计算-计算机加法运算 ← 补码"怎么用"在那篇,本篇讲"为什么"
- 02 信息的数字化表示 ← RGB 三通道的来源
- 01 二进制原理入门 ← 位权是这一切的地基
- 05 编程是什么-给计算机下指令 ← 05 篇埋的"补码"伏笔在此收线
- 📇 计算机基础 · 索引
- 🏠 返回主页