Skip to content
返回博客
教程

进制转换完全指南:二进制、十六进制、八进制与十进制互转

掌握二进制、十六进制、八进制与十进制互转方法,附 JS/Python/Go/C 代码示例和实战场景。免费在线转换工具即试即用。

14 分钟

进制转换完全指南:二进制、十六进制、八进制与十进制互转

下午三点,你在调试器里盯着 0x7FFF5FBFF8C0,切到 CSS 文件改了个 #FF5733,又在终端里敲了 chmod 755。三种不同的数字写法,底层是同一套算术。如果你在十六进制和二进制之间换算时需要停下来想一想,或者好奇 Unix 权限为什么偏偏用八进制——这篇文章能帮你把这些知识串起来。

我们会讲清楚编程中最常见的四种进制、三种值得记住的转换方法,还有 JavaScript、Python、Go 和 C 的实际代码。如果只想快速转个数字,直接打开进制转换工具——支持 2 到 36 任意进制,任意精度。

开发者常用的四种进制

每种进制在编程中存在都有实际原因,不是历史包袱。

二进制(Base 2)——机器的语言

两个数字:01。晶体管要么导通要么截止,这个物理限制决定了二进制的存在。你在处理位掩码、特性标志、位运算和 IP 子网计算时会直接跟二进制打交道。

二进制写起来很长。十进制的 255 在二进制里是 11111111——八位数字才表示一个三位的十进制值。这就是程序员很少直接写二进制的原因,除非每个 bit 的位置都有意义。

八进制(Base 8)——Unix 的速记法

八个数字:07。每个八进制位恰好对应三个二进制位,这就是 Unix 文件权限用八进制的原因——chmod 755 把三组三位权限压缩成三个可读的数字。

八进制在 PDP-11 时代用得更广泛,那时机器字长刚好能被 3 整除。现在它主要出现在 Unix 权限里,偶尔还有 C 语言中以 0 开头的字面量(不少人写 0177 以为是十进制 177,结果踩了坑)。

十进制(Base 10)——人类的默认项

十个数字:09。你的大脑默认用十进制思考——端口号、数组下标、HTTP 状态码、像素尺寸全是十进制。计算机内部并不用十进制,但人类用,所以所有面向用户的数字都以 Base 10 呈现。

十六进制(Base 16)——开发者的瑞士军刀

十六个符号:0-9A-F。每个十六进制位恰好代表四个二进制位(一个 nibble),两个十六进制位就是一个字节,永远如此。

你会在内存地址(0x7FFF5FBFF8C0)、CSS 颜色(#FF5733)、MAC 地址(00:1A:2B:3C:4D:5E)、UUID 格式和哈希摘要中遇到十六进制。它是字节级编程的通用语言。

进制转换工具试一试——输入任意进制的数字,其他进制的结果实时更新。

进制转换的三种核心方法

不用工具也能手动转换,掌握三种方法就够了。

方法一——按位展开(任意进制 → 十进制)

所有位值计数系统的原理相同:每一位的值等于该位数字乘以”进制的该位次方”,从右往左、从 0 开始计数。

二进制 1011

1×2³ + 0×2² + 1×2¹ + 1×2⁰
= 8   + 0    + 2    + 1
= 11

十六进制 FF

15×16¹ + 15×16⁰
= 240   + 15
= 255

代码里大多数语言都有现成的解析函数:

parseInt('1011', 2)   // 11
parseInt('FF', 16)    // 255
parseInt('755', 8)    // 493

方法二——连除取余(十进制 → 任意进制)

反向操作:用十进制数不断除以目标进制,收集余数,从下往上读。

十进制 255 → 十六进制:

255 ÷ 16 = 15  余 15 (F)
 15 ÷ 16 =  0  余 15 (F)
→ 从下往上读:FF

十进制 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
bin(42)    # '0b101010'
hex(255)   # '0xff'
oct(493)   # '0o755'

方法三——位分组(二进制 ↔ 十六进制/八进制直接转)

这是熟练开发者最常用的方法。因为 16 = 2⁴、8 = 2³,二进制和十六进制(或八进制)之间可以通过分组直接转换,不需要做除法。

二进制 → 十六进制: 从右往左每 4 位一组(nibble),不够的左边补零。

二进制:1010 1111
十六进制:A    F
→ AF

二进制 → 八进制: 从右往左每 3 位一组。

二进制:111 101 101
八进制: 7   5   5
→ 755

这张 nibble 对照表值得背下来:

二进制十六进制二进制十六进制
0000010008
0001110019
001021010A
001131011B
010041100C
010151101D
011061110E
011171111F

记住这张表之后,二进制转十六进制就是扫一眼的事。

各语言的进制转换代码

四种语言中进制转换出现频率最高的写法。

JavaScript / TypeScript

// 解析:任意进制字符串 → 数字
parseInt('FF', 16)          // 255
parseInt('101010', 2)       // 42
parseInt('755', 8)          // 493

// 格式化:数字 → 任意进制字符串
(255).toString(16)          // 'ff'
(42).toString(2)            // '101010'
(493).toString(8)           // '755'

// 字面量
const bin = 0b11111111;     // 255
const oct = 0o377;          // 255
const hex = 0xff;           // 255

// BigInt 处理超过 2⁵³ 的值
const big = BigInt('0xFFFFFFFFFFFFFFFF');
big.toString(2)             // 64 个 1
big.toString(10)            // '18446744073709551615'

Python

# 十进制 → 其他进制(返回带前缀的字符串)
bin(255)          # '0b11111111'
oct(493)          # '0o755'
hex(255)          # '0xff'

# 其他进制 → 十进制
int('11111111', 2)    # 255
int('FF', 16)         # 255
int('755', 8)         # 493

# 格式化输出(带填充)
f'{255:08b}'      # '11111111'(8 位二进制,补零)
f'{255:02x}'      # 'ff'(2 位十六进制,小写)
f'{255:02X}'      # 'FF'(2 位十六进制,大写)

# Python 整数天生支持任意精度
big = int('F' * 64, 16)   # 256 位数字,不会溢出

Go

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // 格式化:int → 任意进制字符串
    fmt.Println(strconv.FormatInt(255, 16))  // "ff"
    fmt.Println(strconv.FormatInt(255, 2))   // "11111111"
    fmt.Println(strconv.FormatInt(493, 8))   // "755"

    // 解析:任意进制字符串 → int
    n, _ := strconv.ParseInt("FF", 16, 64)  // 255
    fmt.Println(n)

    // Printf 格式化动词
    fmt.Printf("%b %o %x %d\n", 255, 255, 255, 255)
    // 11111111 377 ff 255
}

C

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main() {
    // 按十进制、八进制、十六进制输出
    printf("%d %o %x\n", 255, 255, 255);
    // 255 377 ff

    // 从任意进制解析
    long val = strtol("FF", NULL, 16);   // 255
    long bin = strtol("101010", NULL, 2); // 42

    // C 没有内置二进制输出——手动逐位提取:
    uint8_t byte = 0xAF;
    for (int i = 7; i >= 0; i--)
        putchar(((byte >> i) & 1) ? '1' : '0');
    // 10101111

    return 0;
}

实战中的进制转换场景

五个进制转换不再是课本知识、而是工作内容的真实场景。

1. 调试内存地址

调试器显示指针在 0x7FFF5FBFF8C0。把最后两个字节转成二进制——1000 1100 0000——可以看出地址对齐到了 64 字节边界(六个尾零)。对齐对缓存性能、SIMD 操作和内存映射 I/O 都有影响。十六进制让这些模式一目了然。

指针算术用十六进制也更好算。基地址偏移 0x100 就是 256 字节——在很多嵌入式系统里刚好是一页。用十进制就没这么直观了。

2. CSS 十六进制颜色 ↔ RGB

颜色 #FF5733 是三个字节按十六进制对排列:

十六进制十进制通道
FFFF255红色(满值)
575787绿色
333351蓝色

简写 #F00 展开为 #FF0000——纯红。八位格式 #FF573380 增加了 alpha 通道,80(十进制 128)大约是 50% 透明度。

理解十六进制和十进制的映射关系,能省掉很多打开取色器微调数值的时间。

3. Unix 文件权限(八进制)

chmod 755 拆解:

7 → 111 → rwx(所有者:读 + 写 + 执行)
5 → 101 → r-x(组:读 + 执行)
5 → 101 → r-x(其他人:读 + 执行)

每个八进制数字编码一组权限,因为三个权限位(读=4,写=2,执行=1)恰好对应一个八进制位(0-7)。常见组合:

八进制二进制权限典型用途
755111 101 101rwxr-xr-x可执行文件、目录
644110 100 100rw-r--r--普通文件
700111 000 000rwx------私有脚本
600110 000 000rw-------SSH 密钥、敏感文件

4. 网络子网计算

/24 子网掩码意味着二进制中有 24 个前导 1:

11111111.11111111.11111111.00000000
→ 255.255.255.0

要算出网络地址,把 IP 和掩码按位 AND:

192.168.1.37   → 11000000.10101000.00000001.00100101
255.255.255.0  → 11111111.11111111.11111111.00000000
AND 结果       → 11000000.10101000.00000001.00000000
               → 192.168.1.0(网络地址)

网络工程师日常在十进制(IP 表示法)、二进制(子网计算)和十六进制(抓包分析)之间来回切换。用进制转换工具可以逐个八位组验算。

5. 读取哈希摘要和 UUID

MD5 哈希如 d41d8cd98f00b204e9800998ecf8427e 是 32 个十六进制字符,代表 16 字节(128 位)。每对十六进制数字就是一个字节。

UUID 遵循 8-4-4-4-12 的十六进制格式:

550e8400-e29b-41d4-a716-446655440000

32 个十六进制数字加连字符——同样是 128 位,只是分段方式不同。第 13 位的 441d4 中的 4)标识这是 UUID v4。

MD5 & SHA 哈希生成器生成哈希,或用 UUID 生成器创建 UUID——两者都输出十六进制,直接对应本文讨论的二进制表示。

超越 Base 16:Base 36、Base 64 和自定义进制

标准四种进制覆盖了大部分工作,但有几种特殊进制也会出现。

Base 36——紧凑的字母数字编码

Base 36 使用 10 个数字加 26 个字母(A-Z),是大小写不敏感下最紧凑的字母数字表示。短链接服务很喜欢它——YouTube 视频 ID、短网址和数据库短键经常用 Base 36。

(1000000).toString(36)    // 'lfls'
parseInt('lfls', 36)      // 1000000

十进制 1,000,000 压缩到四个字符。作为 URL 友好的短标识,很难找到更紧凑的方案。

Base 64——数据编码(不是计数系统)

Base64 看起来像另一种进制,但用途完全不同。它不是把数值用位值系统表示,而是把任意二进制数据(图片、文件、JWT 令牌)编码成 ASCII 文本。使用 A-Z、a-z、0-9、+/——共 64 个符号。

它的数学原理和进制转换不同。Base64 以 3 字节(24 位)为一组输入,输出四个 6 位字符。这是编码方案,不是计数系统。

编解码 Base64 数据可以用Base64 编解码工具

任意进制(2-36)出现的场合

还有几种进制偶尔会冒出来:

  • Base 12(十二进制): 时间(12 小时制)、计量(一打)。有数学家认为 Base 12 比 Base 10 更适合日常使用,因为 12 的因子更多。
  • Base 60(六十进制): 时间(60 秒、60 分钟)和角度(360°)。继承自巴比伦数学。
  • Base 32: Crockford 的 Base32 编码用于人类可读标识符(排除了 I、L、O 等容易混淆的字符),也用于 geohash。

进制转换工具支持 2 到 36 的任意整数进制。

位运算与进制转换

理解二进制不只是为了转数字。位运算建立在二进制之上,而系统编程、游戏开发和权限系统里全是位运算。

const READ  = 0b100;  // 4
const WRITE = 0b010;  // 2
const EXEC  = 0b001;  // 1

// 用 OR 组合权限
const perms = READ | WRITE;  // 0b110 = 6

// 用 AND 检查权限
(perms & READ)  !== 0   // true  — 有读权限
(perms & EXEC)  !== 0   // false — 没有执行权限

// 用 XOR 切换权限
perms ^ WRITE   // 0b100 = 4 — 写权限被移除

// 移位操作
1 << 3    // 0b1000 = 8(1 左移 3 位)
0xFF >> 4  // 0b00001111 = 15(右移 4 位 = 除以 16)

特性标志、硬件寄存器、网络协议、图形编程——全靠位运算。二进制读熟了,位操作就是白纸黑字的逻辑,没什么玄的。

FAQ

编程中最常用的四种进制是什么?

二进制(Base 2)、八进制(Base 8)、十进制(Base 10)和十六进制(Base 16)。二进制是数据在硬件中的物理存在形式,八进制对应 Unix 文件权限,十进制是人类默认的表示方式,十六进制把二进制压缩成可读格式——每个十六进制位恰好是 4 个二进制位。

怎么把二进制转成十六进制?

从右往左每 4 位分成一组,最左边不够的补零。然后查表:0000=0、0001=1、…… 1010=A、…… 1111=F。举例:二进制 10101111 → 分组 1010 1111 → 十六进制 AF。原理是 16 = 2⁴。

怎么把十六进制转成十进制?

每个十六进制位乘以 16 的对应次方(最右边是 0 次方),然后求和。记住 A=10、B=11、C=12、D=13、E=14、F=15。举例:十六进制 FF = 15×16¹ + 15×16⁰ = 240 + 15 = 255。代码中:JavaScript parseInt('FF', 16),Python int('FF', 16)

为什么程序员用十六进制而不用二进制?

十六进制紧凑。每个十六进制位正好对应 4 个二进制位,所以二进制的 11111111 00001010 写成十六进制就是 FF0A——短得多,也更容易扫读。十六进制是内存地址、CSS 颜色(#FF5733)、MAC 地址、哈希输出和 UUID 格式的标准表示。

什么是 nibble?它跟十六进制转换有什么关系?

Nibble 是 4 个二进制位,即半个字节。一个 nibble 恰好对应一个十六进制位,所以二进制转十六进制就是逐 nibble 查表。一个字节 = 两个 nibble = 两个十六进制位。这种整齐的 4 位映射正是十六进制成为字节级数据标准表示的原因。

Unix 文件权限为什么用八进制表示?

每组权限有三个位:读(4)、写(2)、执行(1),共三组(所有者、组、其他人)。因为 2³ = 8,三个权限位恰好对应一个八进制位。755 表示所有者=7(rwx)、组=5(r-x)、其他人=5(r-x)。八进制天然适合 3 位一组的编码。

JavaScript 中怎么处理超过 2⁵³ 的大数字?

BigInt。在数字后加 n 或用 BigInt() 构造函数:BigInt('0xFFFFFFFFFFFFFFFF').toString(2) 会输出完整的 64 位二进制字符串。标准 Number 在超过 9,007,199,254,740,991(2⁵³ - 1)后会丢失精度。我们的进制转换工具内部使用 BigInt,可以处理任意大小的数字。

怎么把十六进制转成二进制?

用 nibble 对照表反向查:每个十六进制位展开成 4 位二进制。0=00001=0001、……、A=1010、……、F=1111。举例:十六进制 AFA1010)+ F1111)→ 二进制 10101111。代码里:JavaScript parseInt('AF', 16).toString(2),Python bin(int('AF', 16))[2:]

八进制怎么直接转十六进制?

两者不能像二进制 ↔ 十六进制那样按位分组直接转,因为 8 和 16 不互为幂次。常规做法是先转为二进制再分组:八进制 755 → 二进制 111 101 101(每位 3 bit)→ 左侧补 0 得 0001 1110 1101 → 十六进制 1ED。或者更简单:用 parseInt('755', 8).toString(16) 一步完成。

Python 2 的 0177 和 Python 3 的 0o177 有什么区别?

Python 2 允许两种写法:01770o177,都表示八进制 127。Python 3 只接受 0o177,裸前缀 0 会报 SyntaxError。这是因为 Python 2 里 0 前缀继承了 C 的语法(容易让 0789 之类的错误被误解为八进制),Python 3 为避免歧义强制显式前缀。迁移老代码时,搜索 \b0[0-7]+\b 模式可以定位所有隐式八进制字面量。