进制转换:计算机与人类沟通的桥梁
速通二进制、八进制、十六进制与十进制的快速互转
引言:掰手指的艺术
小时候学数数,我们从 1 数到 10、到 100,用的是十进制。你有没有想过:为什么偏偏是 10,而不是 7、不是 12?
答案其实非常朴素——因为人有十根手指。十进制从来不是宇宙的真理,它只是”数数”这个动作的历史惯性。
你或许会疑惑,为什么计算机会使用二进制作为基本运算和存储单位,这其实和电路的特性有关,电路只能表示精确的表示两种状态:开(高电平)和关(低电平)。而二进制正好能表示这两种状态,因此二进制成为了计算机的母语。
人用十进制思考,机器用二进制存储,中间就需要一座桥。这座桥就是八进制和十六进制——它们不是第三种、第四种语言,而是二进制的”速记符号”。
一、进制是什么:一切皆”逢几进一”
先看一个你早就烂熟于心的事实:数字 365 里的每个数字,重量并不相同。
365 = 3 × 10² + 6 × 10¹ + 5 × 10⁰
= 300 + 60 + 5
最右边的 5 是”个位”(10⁰),往左依次是十位(10¹)、百位(10²)……每一位的值 = 该位数字 × 进制的位权,而位权就是”进制”的若干次方。十进制满 10 进 1,所以叫”逢十进一”。
二进制完全一样,只是把 10 换成 2,逢二进一:
1101 = 1 × 2³ + 1 × 2² + 0 × 2¹ + 1 × 2⁰
= 8 + 4 + 0 + 1
= 13
八进制逢八进一,十六进制逢十六进一。把它们统一写成数学通式:
任意进制数的值 = Σ(每一位数字 × 该进制的位权)
一句话:进制只是数字的”写法”不同,数值从来没有变过。 十进制的 13、二进制的 1101、八进制的 15、十六进制的 0xD,是同一个数穿了四件不同的衣服。
二、根本大法:二进制与十进制的互转
2.1 十进制 → 二进制:除 2 取余
把 173 转成二进制,记住口诀:除 2 取余,倒着读。
173 ÷ 2 = 86 余 1 ← 最低位
86 ÷ 2 = 43 余 0
43 ÷ 2 = 21 余 1
21 ÷ 2 = 10 余 1
10 ÷ 2 = 5 余 0
5 ÷ 2 = 2 余 1
2 ÷ 2 = 1 余 0
1 ÷ 2 = 0 余 1 ← 最高位
从下往上读:10101101。验证一下:128 + 32 + 8 + 4 + 1 = 173 ✓。
原理很简单:每次除以 2 得到的余数,就是当前最低位的二进制值;剩下的商继续”剥”下一位,剥到 0 为止。除 8、除 16 同理,只是把 2 换成 8、16。
2.2 二进制 → 十进制:按权展开
反过来就更快了——记住一张 2 的幂表,直接心算:
| 2^n | 值 | 2^n | 值 | |
|---|---|---|---|---|
| 2⁰ | 1 | 2⁶ | 64 | |
| 2¹ | 2 | 2⁷ | 128 | |
| 2² | 4 | 2⁸ | 256 | |
| 2³ | 8 | 2⁹ | 512 | |
| 2⁴ | 16 | 2¹⁰ | 1024 | |
| 2⁵ | 32 | 2²⁰ | 1048576 |
比如 110101,从高位往低位念:32 + 16 + 4 + 1 = 53。看到哪个位是 1,就把对应权值加起来,0 直接跳过。练几次之后,8 位以内的二进制你可以在两秒内转成十进制。
TIP这个方法也能用于十进制转二进制
如果你需要转成二进制的十进制数字不是很大,你可以尝试用 2^n 的幂表中的值来进行组合使其加起来等于目标数字。然后,把每个组合的权值写成二进制,就得到了目标数字的二进制表示。
例如:520 = 512 + 8 =
1000001000。
2.3 小数怎么办:乘 2 取整
小数部分换一套口诀:乘 2 取整,顺着读。
把 0.625 转成二进制:
0.625 × 2 = 1.25 → 取 1,剩 0.25
0.25 × 2 = 0.5 → 取 0,剩 0.5
0.5 × 2 = 1.0 → 取 1,剩 0
顺着读:0.101。验证:1/2 + 1/8 = 0.625 ✓。
于是 173.625 = 10101101.101。注意:并非所有十进制小数都能精确转成二进制。比如 0.1 转二进制会得到无限循环的 0.000110011001100...——这里就涉及到了”浮点数”和“精度”的相关概念,这里不再展开。
三、八进制与十六进制:二进制的”缩写”
二进制忠实,但啰嗦。一个 32 位的整数,写成二进制要 32 个字符,谁也记不住。
聪明的古人(程序员)发现:8 = 2³,16 = 2⁴。这意味着:
- 3 位二进制 = 1 位八进制
- 4 位二进制 = 1 位十六进制
于是二进制可以被”分团压缩”成短得多的形式。十六进制特别受欢迎,因为它把 1 个字节(8 位)正好压成 2 个字符。先记住十六进制的字母表:
| 十进制 | 二进制 | 十六进制 |
|---|---|---|
| 0 | 0000 | 0 |
| 1 | 0001 | 1 |
| 2 | 0010 | 2 |
| 3 | 0011 | 3 |
| 4 | 0100 | 4 |
| 5 | 0101 | 5 |
| 6 | 0110 | 6 |
| 7 | 0111 | 7 |
| 8 | 1000 | 8 |
| 9 | 1001 | 9 |
| 10 | 1010 | A |
| 11 | 1011 | B |
| 12 | 1100 | C |
| 13 | 1101 | D |
| 14 | 1110 | E |
| 15 | 1111 | F |
这张表值得背到条件反射。F 是 1111(4 个 1),A 是 1010,C 是 1100——多看一眼,它们就会成为你的老朋友。
四、快速互转的黄金法则
4.1 二进制 ↔ 十六进制:四位一组
以小数点为界,整数部分向左、小数部分向右,每 4 位一组(不足补 0),每组直接对照上表。
拿刚才的 10101101.101 练手:
1010 1101 . 1010 ← 小数部分补 0 凑满 4 位
A D . A
结果:0xAD.A = 173.625 ✓。这就是 C 语言里 0x 前缀的由来——十六进制是给二进制发明的速记本。
4.2 二进制 ↔ 八进制:三位一组
同样的套路,只是把”4 位”换成”3 位”:
10 101 101 . 101 ← 整数部分左侧补 0
2 5 5 . 5
结果:255.5(八进制)= 173.625 ✓。C 语言里以 0 开头的整数是八进制,比如 0755,见下文第六节。
4.3 八进制 ↔ 十六进制:走二进制这座桥
八进制和十六进制之间没有直接对应关系,但谁都能转二进制,所以桥接一下即可:
0xAD → 1010 1101 → 10 101 101 → 0255(八进制)
整个过程 5 秒,比任何”直接公式”都可靠。
4.4 十进制 ↔ 八/十六进制:除 8 取余 / 除 16 取余
跟除 2 取余一模一样,只是除数换成 8 或 16(注意余数 1015 要写成 AF):
173 ÷ 16 = 10 余 13(D)
10 ÷ 16 = 0 余 10(A)
从下往上读:0xAD。其实更快的路径是:先转二进制,再四位分组——反正你总得会二进制。
一张图总结全部路径:
除 2 取余 三位一组 走二进制桥
十进制 ←————————→ 二进制 ←————————→ 八进制 ←————————→ 十六进制
按权展开 四位一组
←————————→
所有进制互转,最终都归结为”除基取余 + 分组对照”两招。
五、心算技巧与常用记忆表
5.1 背下这些”锚点”
0xFF = 255:1 个字节的最大值0xFFFF = 65535:2 个字节(1 个 short)的最大值0x7FFFFFFF = 2147483647:INT_MAX,32 位有符号整数上限0x80000000:32 位补码的INT_MIN(-2147483648)0xFFFFFFFF:32 位无符号最大值,等价于有符号的 -10x40000000:1 GB(1 << 30);0x100000000= 4 GB
有了锚点,陌生数字就能就近推算。看到 0x7FF,立刻知道约等于 2048(2¹¹ - 1)。
5.2 十六进制心算:一分为二
0xA5 = 10×16 + 5 = 165。任何十六进制数转十进制,就是逐位乘以 16 再加:0x3F = 3×16 + 15 = 63。两位数的心算 3 秒,四位数拆成两个两位数再乘 65536。
5.3 二进制心算:从 128 往下数
8 位二进制转十进制,从最高位 128 开始往下试:10110101 → 128 + 32 + 16 + 4 + 1 = 181。熟练后,你看到 1001 1101 会直接反应”9 后面跟 D,那就是 0x9D = 157”——因为你已经学会用十六进制视角看二进制了。
心法:十六进制不是”另一种数”,而是”二进制的 4 位一格视图”。 用这个视角,位运算、掩码、内存地址全部豁然开朗。
六、进制在现实编程中的身影
6.1 C 语言的字面量
int a = 0xAD; // 十六进制:前缀 0x
int b = 0255; // 八进制:前缀 0
int c = 0b10101101; // 二进制:C23 标准支持,GCC/Clang 早已作为扩展支持
经典面试题:010 == 10 吗?不! 010 是八进制,等于 8。这个”0 前缀”的设计每天坑着无数初学者,看到以 0 开头的整数字面量,请立刻想起八进制。
6.2 内存地址与调试器
你在 gdb 或 Windows 调试器里看到的地址,永远长这样:0x7ffd3a2b9c10。用十六进制而不是十进制显示,不是因为程序员装酷,而是因为每个十六进制位恰好对应 4 个二进制位——地址的本质是位模式,十六进制是与它对齐的镜子。
6.3 IP 地址与子网掩码
IPv4 的每个点分数字其实是一个 8 位二进制数:192.168.1.1 就是 11000000.10101000.00000001.00000001。子网掩码 255.255.255.0 更直白——那是 24 个连续的 1,写成 192.168.1.0/24,斜杠后面的 24 就是”前 24 位是网络位”。不看二进制,就永远理解不了 CIDR。
6.4 权限、颜色与 MAC 地址
- chmod 755:
7 = 111(读+写+执行),5 = 101(读+执行)。每个八进制位就是三个二进制开关。 - 网页颜色
#FF0000:红、绿、蓝各占 2 个十六进制位,#FF0000是满红。 - MAC 地址
00:1A:2B:3C:4D:5E:6 个字节,每组 2 个十六进制字符。
6.5 位运算与掩码
这是进制知识回报率最高的地方。想取一个数的最低 4 位?x & 0x0F。想取最低 8 位?x & 0xFF。想把某位置 1?x | (1 << 5)。所有掩码都是十六进制常数,因为它们本质是”连续的 1 排成一行”——0x0F 一眼就能看出是 4 个 1,换成十进制 15 反而要反应一下。
七、思考
0x10为什么是 16,而不是 10? 因为它每一位的权重是 16:1×16¹ + 0×16⁰ = 16。“10”这个写法在不同进制里是不同数值——这正是混用进制时最容易翻车的点。010在 C 语言里等于多少? 8。八进制字面量,经典陷阱。0b11010010对应的十六进制是多少? 四位一组:1101 0010→0xD2= 210。- 十进制的 0.1 能精确写成二进制吗? 不能,它是无限循环小数
0.0001100110011...。这个”装不下”的精度问题,是下一篇文章的主角。
八、总结
| 转换 | 方法 | 记忆 |
|---|---|---|
| 十进制 → 二进制 | 除 2 取余,倒着读 | 整数除法剥位 |
| 二进制 → 十进制 | 按权展开,1 的位权相加 | 背 2 的幂表 |
| 十进制小数 → 二进制 | 乘 2 取整,顺着读 | 小数乘法摘位 |
| 二进制 ↔ 十六进制 | 四位一组,直接对照 | 4 位 = 1 个 0x |
| 二进制 ↔ 八进制 | 三位一组,直接对照 | 3 位 = 1 个八进制位 |
| 八进制 ↔ 十六进制 | 先转二进制,再分组 | 过桥 |
| 十进制 ↔ 八/十六 | 除 8/16 取余,或过二进制 | 同除 2 取余 |
一句话总结全文:
二进制是机器的语言,十进制是人类的习惯,八进制与十六进制是两者的速记本——进制只是数字的皮囊,数值从未改变;而”逢几进一”四个字,就是这整门学问的圆心。
下次你在调试器里看到 0x7ffd3a2b9c10 这样的地址,不要头晕——你能瞬间读出那是”以 16 进制写的位模式”,而再往下钻一层,那里住着的是一个又一个 0 和 1 组成的开关阵列。数字没有变,变的只是写法。
再往深处走一步:有了进制这个地基,下一篇我们聊聊计算机如何用这些 0 和 1 表示”小于 1”的数——以及为什么 0.1 + 0.2 ≠ 0.3。
THAT’S END.