1. 为什么进制转换值得专门写一篇攻略
先说个反直觉的事实:我在网上见过很多学编程两三年的同学,能熟练写各种框架,但一被问到“0.1 在二进制里到底长什么样”就卡壳。进制转换这个东西,看起来是计算机基础课第一章就讲完的内容,可实际上它是整个计算机系统原理里最容易被“自以为懂了”的知识点。
原因也很简单:日常开发里你几乎不会手算进制,IDE、计算器、调试工具全都替你代劳了。但一旦你开始接触底层——内存布局看十六进制 dump、网络协议抓包看字节流、权限系统看八进制掩码、浮点数精度问题追到 IEEE 754 的尾数位——你会发现,所有这些东西的根基都是进制转换。它不是考卷上的一道送分题,而是连接高级语言和底层机器世界的“翻译官”。
另外还有一个很现实的理由。面试的时候,进制转换经常被拿来当压力题:让你现场心算十进制 255 转二进制、八进制转十六进制,或者给你一个 IP 和掩码让你算网络号。这些题本身不难,但紧张状态下容易出错。如果脑子里有一套完整的转换体系,而不是依赖死记硬背,这类题基本就是白送分。
这篇攻略覆盖的内容包括:
- 四种常用进制(二进制、八进制、十进制、十六进制)的本质理解
- 所有进制之间互转的通用方法,而不是零散地记“二进制转八进制三步走”
- 小数部分的转换原理,尤其是二进制小数为什么经常“除不尽”
- 有符号数的补码、原码、反码在进制转换里的实际意义
- 抓包、权限位、颜色编码、内存调试等实战场景中的具体应用
适合人群分两类:第一类是刚接触计算机系统原理的初学者,你需要一个能直接照着操作的完整手册;第二类是已经有开发经验、但想系统补一下底层基础的同学,你可以重点关注原理部分和实战场景章节——那里解释了很多“为什么”而不是“是什么”。
我没打算写成教科书式的条目罗列。文中会有大量具体数字的演算过程、容易踩的坑、以及实操中总结出来的技巧,你可以直接对照步骤来推演。读完之后你再看到一串十六进制内存 dump,应该能条件反射地读出它的含义。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 四个进制到底在说什么:从计数方式看本质
进制转换之所以容易混乱,是因为很多人一开始就没理解“进制”到底在表达什么。我先把这个根基打牢。
2.1 进制的本质:逢 N 进一
无论十进制、二进制还是十六进制,本质都是一种记数规则。十进制是“逢十进一”,二进制是“逢二进一”,八进制是“逢八进一”,十六进制是“逢十六进一”。
举个例子。十进制数 2025,它代表的是:
- 2 在千位,贡献 2 × 10³ = 2000
- 0 在百位,贡献 0 × 10² = 0
- 2 在十位,贡献 2 × 10¹ = 20
- 5 在个位,贡献 5 × 10⁰ = 5
所以 2025 = 2000 + 20 + 5。这里的关键在于:每一位的数字乘上“基数(10)的对应次方”,然后全部加起来。这在数学上叫做“按权展开求和”,权就是那个 10³、10²、10¹、10⁰。
换成二进制数 11111101001,它的每一位就是:
- 从右往左依次是 1×2⁰ + 0×2¹ + 0×2² + 1×2³ + 0×2⁴ + 1×2⁵ + 1×2⁶ + 1×2⁷ + 1×2⁸ + 1×2⁹ + 1×2¹⁰
- 计算一下:1 + 8 + 32 + 64 + 128 + 256 + 512 + 1024 = 2025
看到了吧,同一个数值 2025,在十进制里写作 2025,在二进制里写作 11111101001。它们描述的是同一个“数量”,只是用了不同的“记数语言”。
理解这一点很重要,因为进制转换的本质不是“改变数值的大小”,而是“用另一种语言重新表达同一个数值”。我做转换练习时的心态是:我手里有一个确定的数,我只是在换一种写法。这个心态能避免很多低级错误。
2.2 十六进制为什么存在:二进制的“缩写”
十六进制并不是自然界里常用的计数方式,它纯粹是给人类方便阅读二进制而发明的“缩写方案”。
因为 16 = 2⁴,所以一位十六进制数恰好对应四位二进制数。二进制写起来太长了,一个字节(8 位)在二进制下是 10110010 这种 8 个字符,但用十六进制写就是 B2,只有两个字符。
我当年第一次看到内存 dump 的时候整个人是懵的,满屏都是类似 0x7ffe3a9f4b20 这种地址。后来才意识到,这比一堆 0 和 1 或一个 10 位十进制数可读性好太多了——你能很直观地看出地址的高位和低位变化。十六进制实际上成了底层开发、调试工具、网络协议中的“通用交际语言”。
那八进制呢?它的存在感在现代计算机中已经弱了很多,主要残留在 Unix/Linux 的文件权限里,例如 chmod 755。因为 8 = 2³,一位八进制数正好对应三位二进制数。早期计算机用 12 位、24 位、36 位字长时,八进制很好用,因为位数正好被 3 整除。而现在主流计算机字长是 8 的倍数,三位一组不如四位一组整齐,所以十六进制逐渐取代了八进制。但如果你要处理 Linux 权限、某些网络协议里的字段,八进制依然绕不开。
2.3 位权、基数和数码:三个必须搞清楚的概念
学进制转换,有三个词必须明确区分,否则后面全是糊涂账:
- 基数(Base/Radix):这个进制一共有多少个不同的数码。十进制基数是 10,二进制是 2,八进制是 8,十六进制是 16。
- 数码(Digit):表示某个位置上数字的符号。二进制只有 0 和 1;十六进制除了 0~9,还要借 A~F 代表 10~15。
- 位权(Weight):每个位置对应的“单位值”。十进制小数点左边第一位位权是 10⁰ = 1,第二位 10¹ = 10;二进制小数点左边第一位位权是 2⁰ = 1,第二位 2¹ = 2,以此类推。
弄清这三个概念后,任何进制转十进制就变成了机械操作:把每一位的数码乘以它所在位置的位权,然后累加。这就是通法,比记任何捷径都稳妥。
下面先把“其他进制转十进制”这个最基础的方向讲透。
3. 整数部分转换:从“按权展开”到“短除法”
整数转换是进制互转的第一个环节。我这里给出两个方向的方法,并把它们背后的逻辑讲清楚。
3.1 任何进制转十进制:按权展开求和
这个方法通用于二进制、八进制、十六进制转十进制。规则就是上面说的:把每一位数码与对应位权相乘再相加。
二进制转十进制举例:1101
- 位权从右往左为 2⁰=1、2¹=2、2²=4、2³=8
- 计算:1×8 + 1×4 + 0×2 + 1×1 = 13
八进制转十进制举例:257
- 位权为 8⁰=1、8¹=8、8²=64
- 计算:2×64 + 5×8 + 7×1 = 128 + 40 + 7 = 175
十六进制转十进制举例:3F
- F = 15
- 计算:3×16¹ + 15×16⁰ = 48 + 15 = 63
注意十六进制的 A~F 要先换成 10~15,再参与乘法。这里最容易错的是把 A 当 1,或者把 F 当 16——F 是 15,到 16 就已经进位成 10(十六进制下的 10 等于十进制的 16)。
实操技巧:做这类转换时,我习惯先把位权从右往左写出来(从 1 开始,每次乘基数),再一个一个乘。不要眼算,尤其位数多的时候。脑内很容易因为进位错误而功亏一篑。
3.2 十进制转任何进制:短除法
十进制转其他进制,核心方法是“除基数取余”,通常叫短除法。以十进制 2025 转二进制为例:
- 2025 ÷ 2 = 1012 余 1
- 1012 ÷ 2 = 506 余 0
- 506 ÷ 2 = 253 余 0
- 253 ÷ 2 = 126 余 1
- 126 ÷ 2 = 63 余 0
- 63 ÷ 2 = 31 余 1
- 31 ÷ 2 = 15 余 1
- 15 ÷ 2 = 7 余 1
- 7 ÷ 2 = 3 余 1
- 3 ÷ 2 = 1 余 1
- 1 ÷ 2 = 0 余 1
从下往上读余数,得到 11111101001。跟之前按位权展开的例子里一模一样,说明这个方法是对的。
转八进制就是不断除以 8,转十六进制就是不断除以 16。例如 2025 转八进制:
- 2025 ÷ 8 = 253 余 1
- 253 ÷ 8 = 31 余 5
- 31 ÷ 8 = 3 余 7
- 3 ÷ 8 = 0 余 3
从下往上读:3751。验证一下:3×512 + 7×64 + 5×8 + 1 = 1536 + 448 + 40 + 1 = 2025,正确。
十进制转十六进制同理:2025 ÷ 16 = 126 余 9,126 ÷ 16 = 7 余 14(E),7 ÷ 16 = 0 余 7,所以结果为 7E9。验证:7×256 + 14×16 + 9 = 1792 + 224 + 9 = 2025。
为什么短除法要从下往上读余数? 因为最后一次除法的余数实际上对应的是最高位。第一次除法得到的余数是最低位的数码,因为它是原数除以基数后的“零头”。从下往上读,其实就是把“最高位→最低位”的顺序还原。
常见坑:从下往上读这个方向,几乎每个初学者都会弄反。我的建议是,转完以后用按权展开法再算一遍验证,双保险。这是最有效的防错手段。
3.3 通用性的真正含义
二、八、十六进制的整数转换,其实都围绕同一个流程:
- 其他进制 → 十进制:按权展开
- 十进制 → 其他进制:除基数取余
这两条是所有整数转换的基石。剩下的“二进制 ↔ 八进制/十六进制”之间,可以走“先转十进制再转目标进制”的两步路线,但更常用的方法是分组法,下一节单独讲。
4. 二进制与八进制/十六进制互转:分组法,比你想的更优雅
二进制和八进制/十六进制之间的转换,共享一个底层关系:8 = 2³,16 = 2⁴。因此可以按位分组,一组对一位,不需要经过十进制中转。这是实践中最常用的快捷方式,因为不用做乘法和除法。
4.1 二进制转八进制:三位一组
从右往左,把二进制数每 3 位分成一组,最左边不足 3 位时补 0。然后把每组直接换算成对应的八进制数码。
二进制和八进制的对应关系非常固定:
- 000 → 0
- 001 → 1
- 010 → 2
- 011 → 3
- 100 → 4
- 101 → 5
- 110 → 6
- 111 → 7
举例:二进制 11010110
- 分组:11 010 110,最左边补 0 变成 011 010 110
- 011 → 3,010 → 2,110 → 6
- 结果:八进制
326
验证:11010110 二进制按权展开是多少?1×128 + 1×64 + 0×32 + 1×16 + 0×8 + 1×4 + 1×2 + 0×1 = 214。八进制 326 = 3×64 + 2×8 + 6 = 214。一致。
4.2 二进制转十六进制:四位一组
同样的逻辑,每 4 位一组。对应关系:
- 0000 → 0
- 0001 → 1
- 0010 → 2
- 0011 → 3
- 0100 → 4
- 0101 → 5
- 0110 → 6
- 0111 → 7
- 1000 → 8
- 1001 → 9
- 1010 → A
- 1011 → B
- 1100 → C
- 1101 → D
- 1110 → E
- 1111 → F
举例:二进制 11010110
- 分组:1101 0110(正好 8 位,无需补零)
- 1101 → D,0110 → 6
- 结果:十六进制
D6
你看同样的二进制,转八进制是 326,转十六进制是 D6。这两种写法表达的是同一个数值,只是“翻译”粒度不同。
4.3 八进制/十六进制转二进制:还原分组
反向操作就是刚才的逆过程。每一位八进制数码展开成三位二进制,每一位十六进制数码展开成四位二进制。
举例:八进制 571 转二进制
- 5 → 101
- 7 → 111
- 1 → 001
- 拼接:
101111001
注意第一位 1 展开成了 001,这是必要的,因为每一组都是三位。但展开后在拼接结果的开头,前导 0 可以省略,所以写作 101111001。如果你把中间的零丢掉,结果就会错位。
举例:十六进制 2F 转二进制
- 2 → 0010
- F → 1111
- 拼接:
00101111,去掉前导 0,写作101111
4.4 为什么分组法在计算机里这么常用
因为一个字节(8 位)在十六进制下恰好是两位。内存地址、文件字节流、寄存器值,默认展示方式都是十六进制。你看到一个 0x4A3F,立刻能写出它的二进制:0100 1010 0011 1111。这比先用十进制算一遍快得多,也是一种重要的“机器直觉”。
八进制三位分组在 Unix 权限场景最典型:chmod 755 里的 7 = 111,5 = 101,5 = 101,对应的权限位分别是 rwxr-xr-x。能直接看出三组权限分别属于 owner、group、other,这种直观性正是八进制至今没被完全淘汰的原因。
到此为止,整数部分的进制转换已经全覆盖。接下来是小数的领域,这里才是最容易翻车的地方。
5. 小数部分转换:为什么二进制小数经常“除不尽”
很多人在小数转换上栽跟头,是因为整数转换的方法可以很自然地类推,而小数的逻辑恰好相反,导致认知上会有一个很别扭的拐弯。
5.1 十进制小数转二进制:乘二取整
十进制整数转二进制用“除 2 取余”,而十进制小数转二进制用的是“乘 2 取整”。方向完全相反,原因是小数点右边的位权是 2⁻¹、2⁻²、2⁻³……也就是 0.5、0.25、0.125……
具体步骤:
- 用小数部分乘以 2,取整数部分作为二进制小数的第一位
- 用剩下的小数部分继续乘以 2,重复操作
- 直到小数部分为 0,或者达到需要的精度
- 从上往下读整数部分(即每次乘 2 产生的进位)
举例:十进制小数 0.75 转二进制:
- 0.75 × 2 = 1.5,整数部分取 1,剩 0.5
- 0.5 × 2 = 1.0,整数部分取 1,剩 0
- 结果:
0.11
验证一下位权:0.11 二进制 = 1×2⁻¹ + 1×2⁻² = 0.5 + 0.25 = 0.75。完全正确。
那 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⁻¹ + 0×2⁻² + 1×2⁻³ = 0.5 + 0.125 = 0.625。
5.2 一个反直觉的坑:0.1 永远转不干净
现在来看一个著名问题:十进制 0.1 转二进制。
- 0.1 × 2 = 0.2,取 0
- 0.2 × 2 = 0.4,取 0
- 0.4 × 2 = 0.8,取 0
- 0.8 × 2 = 1.6,取 1,剩 0.6
- 0.6 × 2 = 1.2,取 1,剩 0.2
- 0.2 × 2 = 0.4,取 0
- 0.4 × 2 = 0.8,取 0
- 0.8 × 2 = 1.6,取 1,剩 0.6
- 0.6 × 2 = 1.2,取 1,剩 0.2
看到循环了吗?0.2 → 0.4 → 0.8 → 0.6 → 0.2……从这里开始陷入无限循环。所以十进制 0.1 在二进制下是无限循环小数:0.000110011001100……。同理,0.2、0.3、0.6、0.7 也都是无限循环二进制小数。
这就是为什么你在 JavaScript、Python、C 里做浮点运算时,0.1 + 0.2 不等于精确的 0.3,而得到类似 0.30000000000000004 的结果。计算机只能截取有限位来近似存储这个无限循环小数,误差就藏在最后几位里。
这个例子拿来讲进制转换最有价值,因为它把“纸面上的数学”和“计算机里的现实”连接起来。你理解了 0.1 在二进制里除不尽,就理解了浮点数精确性问题的根源。这不是什么玄乎的黑魔法,只是进制转换的自然推论。
实操技巧:如果你要手工把十进制小数转二进制到某个精度,通常转换到小数点后 8 位左右就足够满足日常理解。如果你在做协议解析或嵌入式开发,按数据类型精度来确定位数:单精度浮点尾数 23 位,双精度 52 位,按需截断。
5.3 小数转八进制/十六进制:乘 8 取整、乘 16 取整
同样的“乘基数取整”法,对八进制和十六进制一样适用。
0.75 转八进制:
- 0.75 × 8 = 6.0,整数部分取 6,剩 0
- 结果:
0.6
0.75 转十六进制:
- 0.75 × 16 = 12.0,整数部分取 12(十六进制中是 C),剩 0
- 结果:
0.C
再复杂一点:0.1 转十六进制:
- 0.1 × 16 = 1.6,取 1,剩 0.6
- 0.6 × 16 = 9.6,取 9,剩 0.6
- 从这开始循环:0.6 × 16 = 9.6……
- 结果:
0.199999……,同样是无限循环
这个现象在二进制世界完全合理:十进制 0.1 的分母是 10,而 10 的因子包含 2 和 5;二进制只认 2 这个因子,所以任何包含 5 因子的小数(如 1/5、1/10)都“转不干净”。反过来,二进制的有限小数转十进制却一定是有限的,因为分母只有 2 的幂。
5.4 二进制小数转十进制:按位权展开
反向操作是最简单的:把小数点右边每一位按 2⁻ⁿ 展开求和。
二进制 0.101 转十进制:
- 1×2⁻¹ + 0×2⁻² + 1×2⁻³ = 0.5 + 0.125 = 0.625
用同样的方式可以把八进制、十六进制小数转十进制。
我在面试中常看见有人卡在“小数乘基取整和整数除基取余”方向搞混。一个简便记忆法:整数部分是从右侧低位往左侧高位进位,所以除以基数来“剥离”低位;小数部分是从左侧往右侧产生小数位,所以乘以基数来“溢出”高位整数。本质上,除法和乘法的选择是由“要生成的位在整数方向还是小数方向”决定的。
至此,进制转换的核心算法已经全部覆盖。下一步解决另一个常常被忽视的问题:负数怎么玩。
6. 负数与有符号数:原码、反码、补码的世界
如果你只在非负整数范围内做进制转换,会错过一个非常重要的维度。因为计算机里的负数,不只是在二进制前面加一个负号那么简单。
6.1 计算机为什么用补码表示负数
一个 8 位二进制数,如果最高位表示符号(0 为正、1 为负),那 +5 就是 00000101,-5 就是 10000101。这种表示叫做“原码”。但原码有两个问题:
- 存在两个 0:
00000000是 +0,10000000是 -0,这很浪费 - 加减运算复杂:要让正数和负数直接相加,需要单独设计加减法电路,还要判断符号
于是发明了补码。补码的规则:正数的补码就是原码本身;负数的补码是“原码的各位取反再加 1”。取反加 1 这个操作,等价于数学上的“模运算”结果。
我举个例子:8 位二进制中,-5 的补码怎么算?
- 5 的二进制是
00000101 - 取反:
11111010 - 加 1:
11111011
所以 -5 在 8 位补码里写作 11111011(十六进制写作 0xFB)。
为什么这么做?因为给补码加一个正数可以直接用普通加法电路完成:
- 5 + (-5) = 00000101 + 11111011 = 100000000,8 位溢出了,低 8 位是 00000000,结果 0,正确。
这就是补码的精妙之处:不需要单独的减法器,溢出截断后自然得到正确结果。计算机里一切减法都变成加法,这极大简化了硬件设计。你做进制转换时,如果只转换正整数,就看不到这一层设计逻辑。
6.2 有符号数的进制转换实操
题目常见的考法是:给你一个十六进制数,让你判断它对应多少十进制。
关键看这个数是当作有符号还是无符号。以 8 位为例:
0xFF无符号是 2550xFF有符号(补码)是 -1
判断规则是看最高位(符号位)。最高位为 1,则这个数在有符号语境下是负数;否则是正数。
负数转十进制有两条路:
- 先求补码的补码(也就是取反加一),得到绝对值,再添负号
- 直接用公式:
-1 × 2^(n-1) + 剩下 n-1 位按无符号计算
举例:0xFB 转有符号十进制。
- 方式一:0xFB 二进制
11111011,取反得00000100,加 1 得00000101= 5,因此它是 -5 - 方式二:最高位贡献 -128,低 7 位
1111011是 123,-128 + 123 = -5
两种方式结果一致,任选一种。
6.3 十六进制补码里最容易犯的错
十六进制转二进制再转补码时,常见的坑是“取反”时丢了前导零。比如 8 位 0x05 是 00000101,取反应该对完整 8 位取,得 11111010,再加 1 得 11111011。如果你只对 101 取反变成 010 再加 1 得 011,那就全错了。
所以在做任何补码操作之前,先明确位数:是 8 位、16 位还是 32 位?写成完整的位数再动手。这不仅是做题的规范,也是实际编程中阅读调试器数据时的基本素养。
6.4 进制转换和位运算的联动
掌握了补码之后,位运算的很多行为就好理解了:
- 右移运算在 C/C++ 中对有符号数默认是算术右移(符号位填充)
- Python 的负数右移也按补码逻辑进行
这些都是补码表示的直接推论。做进制转换练习时,如果能顺手把这些概念串起来,你对计算机系统原理的理解会上一个新台阶——进制不是孤立的算术题,而是底层实现的基础语言。
7. 实战场景:抓包、权限、颜色、内存调试中的进制身影
学进制转换最怕的是“会做题但不知道有什么用”。这一节我把常见的实战场景串一遍,你会发现进制转换几乎无处不在。
7.1 网络抓包与十六进制字节流
用过 Wireshark 的人都知道,抓包面板中间那一栏就是十六进制字节流。每条网络报文就是一串字节,每个字节用两个十六进制数表示。
比如一个 TCP 报文,你看到 45 00 00 3c,第一个字节 0x45 就包含了 IP 头前两个字段:4 位版本号 + 4 位首部长度。0x45 转二进制是 01000101,前四位 0100 是版本 4(IPv4),后四位 0101 是 5,表示首部长度为 5 × 4 = 20 字节。
随便一个字段都能拆出这样的信息。你说你不会进制转换,那看到 0x45 就只能干瞪眼。分组法的价值在这一刻体现得淋漓尽致——4 位一组就等于一位十六进制,拆位是瞬间的事。
7.2 Linux 文件权限的八进制基因
chmod 755 这个命令大家天天敲,但 755 为什么代表 rwxr-xr-x?
八进制 7 = 二进制 111,三位分别对应读(r)、写(w)、执行(x)权限位。5 = 101,表示有读和执行,没有写。
所以 755 拆开看就是:
- owner:7 = 111 = rwx
- group:5 = 101 = r-x
- other:5 = 101 = r-x
如果你遇到一个权限 6,那就是 110 = rw-。这不是背出来的,是八进制和二进制三位对应关系直接翻译的。八进制在权限系统的保留,就是因为它和权限位的 3 bit 结构完美匹配。
7.3 前端颜色与十六进制
#FF5733 大家可能天天见。这个十六进制颜色码,前两位 FF 是红色通道,中间两位 57 是绿色通道,后两位 33 是蓝色通道,每个通道范围 0~255,正好对应一个字节。
所以 #FF5733 可以拆成三个十进制数:255、87、51。你在设计系统里调整颜色时,十六进制和十进制的互转几乎是必备技能。为什么选十六进制而不是十进制?因为 #FF 比 255 更能直观表达“两个位全满”,而且三个通道各占两个字节,布局非常规整。
7.4 内存调试与地址解析
调试器里看内存地址,清一色十六进制:0x7ffd3b9f16ec。要判断地址对齐情况(比如是不是 8 字节对齐),实际是看十六进制地址的最低位是不是 0 或 8。因为 8 字节对齐意味着地址是 8 的倍数,二进制下低 3 位必须是 0,而三位一组在十六进制里正好映射到最低位的四个 bit 中——如果最低 hex 位是 0、8 以外的值,就不可能被 8 整除。
再比如,结构体字段在内存里按偏移存放,每个偏移地址在 dump 里都是相对基址的位移。你能熟练把十六进制偏移转成二进制,再判断某个 bit 是否置位,排查寄存器标志位时效率会高很多。
实操技巧:我平时排查标志位问题时,会先在心里把十六进制的一位展开成 4 位二进制,然后逐位检查。例如
0x3=0011,表示低两位都是 1。这种条件反射刚开始需要刻意练习,一旦形成,看状态寄存器的速度会快一个量级。
8. 常见错误、验证手段与心算技巧
很多人觉得进制转换难,其实不是原理难,而是“做完不敢确认对不对”。所以我专门总结一套快速验证方法和心算技巧。
8.1 五个高频错误
根据我观察到的现象,大多数人做进制转换时集中在下面几个错误上:
- 方向弄反:短除法算完,从下往上 vs 从上往下读错
- 补零遗漏:二进制转八进制/十六进制时,最左边不足一组不补 0,导致结果少位
- 十六进制数码混淆:把 A 当 1,把 F 当 15 却错写成 16
- 小数方向错误:该乘 2 取整时用了除 2 取余
- 有符号/无符号不区分:看到
0xFE就直接说是 -2,忘了这可能只是无符号 254
8.2 快速验证技巧
做完转换后,最有效的验证方式是做回转换:把结果按位权展开再转回十进制,看看是否等于原始值。这个方法看起来多一步,但能抓住绝大部分错误。
另一个验证技巧是“末位法则”:
- 偶数转二进制,末位一定是 0;奇数末位一定是 1
- 能被 4 整除的数,二进制末两位一定是 00
- 能被 16 整除的数,十六进制末位一定是 0
这些规则虽然不能完全验证整个结果,但能快速筛掉明显错误。
8.3 心算幂次:一张值得记住的表
进制转换的心算速度,很大程度上取决于你对 2 的幂次的熟悉程度。下面这张表值得熟记:
| 2 的幂次 | 十进制值 |
|---|---|
| 2⁰ | 1 |
| 2¹ | 2 |
| 2² | 4 |
| 2³ | 8 |
| 2⁴ | 16 |
| 2⁵ | 32 |
| 2⁶ | 64 |
| 2⁷ | 128 |
| 2⁸ | 256 |
| 2⁹ | 512 |
| 2¹⁰ | 1024 |
| 2¹¹ | 2048 |
| 2¹² | 4096 |
| 2¹³ | 8192 |
| 2¹⁴ | 16384 |
| 2¹⁵ | 32768 |
| 2¹⁶ | 65536 |
记住 2¹⁰ = 1024(也就是 1K),这个数值在计算机领域无比重要。2¹⁶ = 65536,是 16 位无符号整数的最大值加一,也是 IPv4 一个端口范围的大小。这些数字不只是转换题,还是系统原理里的常识。
还有一个常用的速查关系:十六进制每一位的最大值 0xF = 15,对应十进制 15,二进制 1111。八进制每一位最大值 7,对应二进制 111。
8.4 心算一个复杂例子
用 2025 转十六进制来说明心算过程:
- 2048 比 2025 小,2¹¹ = 2048,但大了,所以最高位是 2¹⁰ = 1024,也就是十六进制的第 3 位(从 0 开始是 0x?)不是直接看,而是拆减
- 2025 - 1024 = 1001
- 再找小于 1001 的最大 2 的幂:512(2⁹),1001 - 512 = 489
- 489 - 256 = 233
- 233 - 128 = 105
- 105 - 64 = 41
- 41 - 32 = 9
- 9 - 8 = 1
- 1 - 1 = 0
- 所以二进制是 11111101001,转换为十六进制:0111 1110 1001 = 0x7E9
这个方法叫“减去最大 2 的幂”分解,是心算二进制和十六进制最快的方式,也不需要做除法。对于像 2025 这样的数,多试几次就能快速反应。
9. 从进制到计算机系统原理:一次延伸认知的总结
文章一路讲到这里,该到把进制转换放回整个计算机系统原理的坐标系里去看它真正位置的时候了。
很多人以为进制转换只是“考试工具”,学完就丢。但如果你稍微往底层走一步,就会发现在计算机系统原理里,进制几乎无处不在,它是理解底层世界的一把钥匙:
- CPU 和内存的地址寻址:地址总线宽度决定了可寻址空间,而 32 位地址空间是 2³² = 4GB,64 位则大到无法用直觉感受。这个“2 的幂次”的心算能力,就是你对系统规模的直觉。
- 浮点数表示:IEEE 754 标准把浮点数拆成符号位 + 指数位 + 尾数位,全是位域结构。理解位域的前提,就是能流畅地在二进制和十六进制之间切换。
- 网络地址规划:IP 地址的掩码、子网划分、CIDR 表示法,本质都是二进制位操作。你能把
255.255.255.0转成二进制,就能理解为什么一个 C 类网段是 256 个地址。 - 编码体系:ASCII、UTF-8、GBK、Base64,这些编码全是对字节流的操作。Base64 为什么是 64?因为它恰好用 6 位二进制表示一个字符(2⁶ = 64)。看懂了进制,编码就不再神秘。
- 数据压缩与加密:位运算、位移、掩码是压缩算法和加密算法的基础单元。你要调一个 bit 级别的协议,进制转换能力直接决定效率。
从我自己学习路径的体会来说,把进制转换当成“阅读理解”而不是“算术题”,认知深度会完全不同。算术题做完就完了,阅读理解能让你看到数字背后那个机器的运行逻辑。
9.1 推荐的自测清单
文章末尾,我整理了一份自测清单,方便你看看自己是否真的掌握了进制转换。建议别跳过,逐一在脑内或草稿纸上验证:
- [ ] 能否口算 0~255 任意十进制数对应的十六进制?
- [ ] 能否把
0xFF00立刻断定为 IPv4 里常用的掩码(255.255.0.0)? - [ ] 能否解释为什么
0.1 + 0.2在浮点数里不等于 0.3? - [ ] 能否从内存 dump 里读出一个 int32 的值(注意大小端)?
- [ ] 能否快速判断一个十六进制数是正数还是负数(在有符号语境下)?
- [ ] 能否手算十进制 3.14 转二进制到小数点后 6 位?
- [ ] 能否解释为什么 Unix 权限用八进制而不是十进制?
- [ ] 能否写出
chmod 644对应的权限位并解释其安全性?
这些问题覆盖了进制转换的各个层面。如果你能做到大半以上,说明进制互转对你已经不是障碍,而是思维方式的一部分。
9.2 大小端:进制转换之外的最后一课
最后补一个实战中必然会碰到的细节:大小端。它在进制转换的范畴里不算严格主题,但读到内存 dump 时绕不开。
- 小端(Little-Endian):低字节存放在低地址。Intel x86、ARM 默认小端
- 大端(Big-Endian):高字节存放在低地址。网络字节序是大端
举例:int32 值 0x12345678,在小端内存里存放是 78 56 34 12,大端是 12 34 56 78。在抓包软件或者调试器里看到这样的字节顺序,你要能意识到这跟相对地址和大小端有关,否则极易把数值读错。
这其实再次说明进制转换不是孤立的“数学课”,它跟计算机系统底层到处连在一起。
9.3 我的个人习惯
按我自己的经验,进制转换能力的真正提升,不是靠大量刷题目,而是靠在日常工作中主动“切换视角”:
- 看到端口号 8080,我会下意识想它十六进制是多少:0x1F90
- 看到一个 IPv4 地址 192.168.1.1,我会想每个段的二进制:11000000.10101000.00000001.00000001
- 看到一个颜色
#336699,我会想三个通道的十进制分别是 51、102、153
这种习惯一旦养成,进制转换就不再是“要不要算”的问题,而是像母语一样自然。希望这篇攻略能帮你跨过从“死记硬背”到“理解本质”的那道坎。
