进制转换 -- 二进制、十进制、十六进制
本讲你将掌握三种进制之间的转换方法,并理解十六进制为什么是二进制的「最佳缩写」。
生活化类比:多套计数系统
不同进制就像不同的「度量衡」。
人类习惯十进制,就像习惯用「米」来量身高。
计算机使用二进制,就像用「开关状态」来思考。
而十六进制,则是工程师的「速记符号」——用更短的方式表达冗长的二进制。
举个例子:「RUNOOB」这个词如果在三种「语言」中表达:
- 十进制的人类语言:82 85 78 79 79 66
- 二进制的机器语言:01010010 01010101 01001110 01001111 01001111 01000010
- 十六进制的简写:52 55 4E 4F 4F 42
同样是一句话,二进制的表达长度是十六进制的 4 倍。
这就是我们需要学习进制转换的原因。
进制的本质:按权展开
所有进制的原理完全相同——每个位置上的数字乘以基数的幂,然后全部加起来。
以数字 42 为例,看它在三种进制下分别如何被理解:
| 进制 | 基数 | 可用数字 | 表示 | 按权展开 |
|---|---|---|---|---|
| 十进制 | 10 | 0 ~ 9 | 42 | 4 x 101 + 2 x 100 = 42 |
| 二进制 | 2 | 0, 1 | 101010 | 1x25+0x24+1x23+0x22+1x21+0x20 = 42 |
| 十六进制 | 16 | 0~9, A~F | 2A | 2 x 161 + 10 x 160 = 42 |
关键规律:进制的「基数」决定了每一位的权重。
十进制基数 10,个位权重 100=1,十位 101=10,百位 102=100。
二进制基数 2,从右往左权重为 1, 2, 4, 8, 16, 32, 64, 128。
十六进制基数 16,从右往左权重为 1, 16, 256, 4096。
理解了「按权展开」,所有进制转换都变得简单了。你需要记住的只有一句话:每位数字 x 基数^位置编号,然后求和。
方法一:十进制转二进制(除 2 取余法)
假设你要把十进制的 42 转为二进制。
方法很机械:
- 用 42 除以 2,商 21 余 0
- 用 21 除以 2,商 10 余 1
- 用 10 除以 2,商 5 余 0
- 用 5 除以 2,商 2 余 1
- 用 2 除以 2,商 1 余 0
- 用 1 除以 2,商 0 余 1
- 从下往上读余数:101010
为什么这个方法有效?
二进制的本质是:N = d0x20 + d1x21 + d2x22 + ...
当 N 除以 2 时,余数就是 d0(最低位),商继续除 2 得到下一位。
这个逻辑和小学学过的「十进制除 10 取个位」完全一样。
方法二:二进制转十进制(按权展开法)
从二进制转回十进制更简单——直接按权重加起来。
二进制 101010(从右往左编号位置 0~5):
| 位置 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|
| 二进制位 | 1 | 0 | 1 | 0 | 1 | 0 |
| 权重 (2^n) | 32 | 16 | 8 | 4 | 2 | 1 |
| 位 x 权重 | 1x32 | 0x16 | 1x8 | 0x4 | 1x2 | 0x1 |
1x32 + 0x16 + 1x8 + 0x4 + 1x2 + 0x1 = 42
十六进制:二进制的「速记符号」
二进制的最大问题是写起来太长。
一个 8 位的值要写 8 个数字,32 位则要写 32 个数字。
这既不便于人类阅读,也容易出错。
十六进制解决了这个问题,因为一个重要的数学巧合:
24 = 16 → 每 4 个二进制位正好对应 1 个十六进制位
4 位二进制与 1 位十六进制的映射表
| 二进制 (4位) | 十进制 | 十六进制 | 二进制 (4位) | 十进制 | 十六进制 |
|---|---|---|---|---|---|
| 0000 | 0 | 0 | 1000 | 8 | 8 |
| 0001 | 1 | 1 | 1001 | 9 | 9 |
| 0010 | 2 | 2 | 1010 | 10 | A |
| 0011 | 3 | 3 | 1011 | 11 | B |
| 0100 | 4 | 4 | 1100 | 12 | C |
| 0101 | 5 | 5 | 1101 | 13 | D |
| 0110 | 6 | 6 | 1110 | 14 | E |
| 0111 | 7 | 7 | 1111 | 15 | F |
这张表是十六进制的核心。
熟记之后,任何二进制转十六进制都是「查表取字」的机械操作。
示例:二进制 11111111 转十六进制
8 位二进制分为两组,每组 4 位:
在编程中,
0x前缀表示十六进制(如0xFF),0b前缀表示二进制(如0b11111111)。这些前缀告诉编译器:这不是十进制数。
交互演示:三栏同步转换器
下面这个转换器让你实时观察三种进制之间的对应关系。
在任意一栏输入数值,其余两栏会自动同步更新。
注意观察二进制栏下方的色块高亮——它展示了4 位一组对应 1 位十六进制的关系。
十进制 Decimal
二进制 Binary
十六进制 Hex
试试看:在十进制栏输入 255,观察二进制栏的全 1 和十六进制栏的 FF。
这就是「一个字节的最大值」——理解了这个,很多概念就通了。
代码演示:完整的进制转换
实例
def decimal_to_binary(n):
"""十进制转二进制:除2取余法,展示完整步骤"""
if n == 0:
return "0"
steps = []
original = n
while n > 0:
remainder = n % 2
quotient = n // 2
steps.append((n, quotient, remainder))
n = quotient
result = ''.join(str(r) for _, _, r in reversed(steps))
print(f"{original} 转为二进制:")
for val, quot, rem in steps:
print(f" {val:3d} ÷ 2 = {quot:3d} 余 {rem}")
print(f" → 结果: {result}")
return result
def binary_to_decimal(bin_str):
"""二进制转十进制:按权展开法"""
decimal = 0
length = len(bin_str)
print(f"二进制 {bin_str} 按权展开:")
for i, ch in enumerate(bin_str):
power = length - 1 - i
if ch == '1':
value = 2 ** power
decimal += value
print(f" 位{power}: 1 x 2^{power} = {value}")
print(f" → 总和: {decimal}")
return decimal
def bin_to_hex(bin_str):
"""二进制转十六进制:每4位一组查表"""
while len(bin_str) % 4 != 0:
bin_str = '0' + bin_str
hex_map = {
'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'
}
result = ''
groups = []
for i in range(0, len(bin_str), 4):
group = bin_str[i:i+4]
hex_char = hex_map[group]
groups.append(f"{group} → {hex_char}")
result += hex_char
print(f"二进制 {bin_str} 转十六进制:")
for g in groups:
print(f" {g}")
print(f" → 0x{result}")
return result
def hex_to_bin(hex_str):
"""十六进制转二进制:每位展开为4位"""
rev_map = {
'0':'0000','1':'0001','2':'0010','3':'0011',
'4':'0100','5':'0101','6':'0110','7':'0111',
'8':'1000','9':'1001','A':'1010','B':'1011',
'C':'1100','D':'1101','E':'1110','F':'1111'
}
hex_str = hex_str.upper()
result = ''
for ch in hex_str:
expanded = rev_map[ch]
result += expanded
print(f" {ch} → {expanded}")
# 去掉前导零(但至少保留一位)
result = result.lstrip('0') or '0'
print(f" → {result}")
return result
# 综合测试:以 RUNOOB 字符 'R' 的 ASCII 码 82 为中心
print("=" * 50)
print("测试值: 82 (ASCII 'R' from RUNOOB)")
print("=" * 50)
bin_result = decimal_to_binary(82)
print(f"82 → 二进制: {bin_result}\n")
dec_result = binary_to_decimal(bin_result)
print()
hex_result = bin_to_hex(bin_result)
print()
print("反向验证:十六进制 → 二进制")
hex_to_bin(hex_result)
print("\n" + "=" * 50)
print("测试值: 255 (一个字节的最大值)")
print("=" * 50)
bin_255 = decimal_to_binary(255)
print(f"二进制: {bin_255}")
hex_255 = bin_to_hex(bin_255)
print(f"十六进制: 0x{hex_255}")
print("注意:255 的二进制是8位全1,十六进制只用了2位 FF")
进阶:十六进制到十进制的快速转换
你已经会了两种转十进制的方法:一是按权展开(每个十六进制位 x 16 的幂),二是先转二进制再转十进制。
但还有一种更实用的方法——以二进制为「中间语言」。
任何进制转换都可以通过二进制作为桥梁:
因为十进制和十六进制之间的直接转换计算量较大,但各自与二进制的转换都非常简单。
十进制转二进制:除 2 取余。
十六进制转二进制:查表展开(每 1 位变 4 位)。
