07.进制转换上篇:位权展开与十进制互转

进制转换上篇:位权展开与十进制互转

二进制、八进制、十进制和十六进制看似是四套不同的数字,实质上只是同一个数值的不同书写方式。掌握基数、数码和位权后,任意进制转十进制可以用位权展开,十进制整数转其他进制可以用除基取余,小数部分则使用乘基取整。本文从原理出发,完整推导整数、小数、混合数与任意进制转换,并解释有限精度和误差从何而来。

一、计算机为什么使用二进制

计算机中的数字、字母、图片和声音最终都要编码为二进制数据。使用二进制并不是因为人类更容易阅读 0 和 1,而是因为数字电路更容易可靠地区分两种状态,例如高电平与低电平、导通与截止。

需要区分两个概念:

  • 数值数据 具有明确的算术意义,例如整数 86、小数 3.14;
  • 非数值信息 也可以编码成二进制,例如字符 A、图片像素和机器指令,但对应的比特模式要按特定编码规则解释。

例如,二进制位模式 01000001:

  • 按无符号整数解释是十进制 65;
  • 按 ASCII 字符编码解释是字母 A;
  • 它本身只是一组比特,真正含义取决于数据类型和使用场景。

因此,"计算机内部都是 0 和 1"不等于"所有 0 和 1 都是二进制整数"。

二、进位计数制的三个基本概念

1. 基数

基数表示一种进制可使用多少个不同的基本数码。基数为 r 的计数制称为 r 进制。

进制 基数 可用数码 进位规则
二进制 2 0、1 逢二进一
八进制 8 0~7 逢八进一
十进制 10 0~9 逢十进一
十六进制 16 0~9、A~F 逢十六进一

十六进制中:

text 复制代码
A = 10
B = 11
C = 12
D = 13
E = 14
F = 15

字母大小写不影响数值,A 与 a 都表示十进制 10。

2. 数码

数码是书写该进制时允许使用的基本符号。1021 是合法的三进制数,因为其中每一位都小于 3;1031 不是合法三进制数,因为数码 3 不属于三进制。

一般地,r 进制中每一位数码 d 必须满足:

text 复制代码
0 <= d < r

因此:

  • 二进制不允许出现 2;
  • 八进制不允许出现 8 或 9;
  • 十六进制不允许出现 G。

3. 位权

同一个数码放在不同位置,表示的值不同。位置对应的权值称为位权。

十进制 523 可以展开为:

text 复制代码
523 = 5 x 10^2 + 2 x 10^1 + 3 x 10^0

从小数点向左,位权依次是:

text 复制代码
... r^3, r^2, r^1, r^0

从小数点向右,位权依次是:

text 复制代码
r^-1, r^-2, r^-3, ...

无论什么进制,都遵循同一套位置计数规则。

三、位置计数法的统一公式

一个带小数部分的 r 进制数可以写成:

text 复制代码
(d_n d_(n-1) ... d_1 d_0 . d_(-1) d_(-2) ... d_(-m))_r

其数值为:

text 复制代码
d_n x r^n + ... + d_1 x r^1 + d_0 x r^0
+ d_(-1) x r^-1 + d_(-2) x r^-2 + ... + d_(-m) x r^-m

下标 _r 用来说明原数的进制。例如:

text 复制代码
(1011)_2
(526)_8
(523)_10
(5A3B)_16

如果上下文容易混淆,必须标明进制。单独写 10 时,它可能表示:

text 复制代码
(10)_2  = 2
(10)_8  = 8
(10)_10 = 10
(10)_16 = 16

数码串相同,基数不同,实际数值也不同。

四、其他进制转十进制:位权展开法

转换原则是:

  1. 从小数点开始确定每一位的指数;
  2. 用每位数码乘以对应位权;
  3. 把所有结果相加。

1. 二进制整数转十进制

以课堂笔记中的 (1010110)_2 为例:

text 复制代码
(1010110)_2
= 1 x 2^6 + 0 x 2^5 + 1 x 2^4 + 0 x 2^3
  + 1 x 2^2 + 1 x 2^1 + 0 x 2^0
= 64 + 0 + 16 + 0 + 4 + 2 + 0
= 86

所以:

text 复制代码
(1010110)_2 = (86)_10

初学者最容易把最右侧位的指数写成 1。实际上个位的位权始终是 r^0 = 1。

2. 八进制整数转十进制

text 复制代码
(526)_8
= 5 x 8^2 + 2 x 8^1 + 6 x 8^0
= 5 x 64 + 2 x 8 + 6 x 1
= 320 + 16 + 6
= 342

所以:

text 复制代码
(526)_8 = (342)_10

3. 十六进制整数转十进制

text 复制代码
(5A3B)_16
= 5 x 16^3 + 10 x 16^2 + 3 x 16^1 + 11 x 16^0
= 5 x 4096 + 10 x 256 + 3 x 16 + 11
= 20480 + 2560 + 48 + 11
= 23099

因此:

text 复制代码
(5A3B)_16 = (23099)_10

处理十六进制时,要先把 A~F 换成 10~15,再参与位权计算。

五、带小数部分的位权展开

1. 二进制小数

text 复制代码
(101.101)_2
= 1 x 2^2 + 0 x 2^1 + 1 x 2^0
  + 1 x 2^-1 + 0 x 2^-2 + 1 x 2^-3
= 4 + 0 + 1 + 0.5 + 0 + 0.125
= 5.625

所以:

text 复制代码
(101.101)_2 = (5.625)_10

二进制小数点后的位权依次为:

text 复制代码
2^-1 = 1/2
2^-2 = 1/4
2^-3 = 1/8
2^-4 = 1/16

2. 八进制小数

text 复制代码
(17.34)_8
= 1 x 8^1 + 7 x 8^0 + 3 x 8^-1 + 4 x 8^-2
= 8 + 7 + 3/8 + 4/64
= 15.4375

3. 十六进制小数

text 复制代码
(A.C)_16
= 10 x 16^0 + 12 x 16^-1
= 10 + 12/16
= 10.75

小数点左侧最靠近小数点的一位是 r^0,右侧最靠近小数点的一位是 r^-1,两边不要同时从 0 开始。

六、用霍纳法减少乘方计算

直接位权展开容易理解,但位数很多时反复计算幂比较繁琐。整数部分可以使用霍纳法从左到右累计:

text 复制代码
当前结果 = 当前结果 x 基数 + 下一位数码

仍以 (5A3B)_16 为例:

text 复制代码
初始结果 = 0
读入 5: 0 x 16 + 5  = 5
读入 A: 5 x 16 + 10 = 90
读入 3:90 x 16 + 3  = 1443
读入 B:1443 x 16 + 11 = 23099

写成嵌套形式:

text 复制代码
(((5 x 16 + 10) x 16 + 3) x 16 + 11) = 23099

程序把字符串形式的进制数转换成整数时,经常使用这一思路,因为它只需要按顺序扫描数码并持续执行乘加操作。

七、十进制整数转其他进制:除基取余法

转换十进制非负整数 N 到 r 进制:

  1. 用 N 除以基数 r;
  2. 记录余数,它是结果当前的最低位;
  3. 用商继续除以 r;
  4. 直到商为 0;
  5. 把余数从后向前倒序排列。

之所以要倒序,是因为第一次得到的是最低位,最后一次得到的是最高位。

1. 十进制 86 转二进制

text 复制代码
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

余数按产生顺序是 0 1 1 0 1 0 1,逆序得到:

text 复制代码
(86)_10 = (1010110)_2

反向检查:

text 复制代码
2^6 + 2^4 + 2^2 + 2^1
= 64 + 16 + 4 + 2
= 86

2. 十进制 86 转八进制

text 复制代码
86 / 8 = 10 ... 6
10 / 8 =  1 ... 2
 1 / 8 =  0 ... 1

倒序读取余数:

text 复制代码
(86)_10 = (126)_8

验证:

text 复制代码
1 x 8^2 + 2 x 8^1 + 6 x 8^0
= 64 + 16 + 6
= 86

3. 十进制 86 转十六进制

text 复制代码
86 / 16 = 5 ... 6
 5 / 16 = 0 ... 5

因此:

text 复制代码
(86)_10 = (56)_16

4. 转换到超过十进制的任意基数

当余数大于 9 时,要用约定字母表示。例如十进制 250 转十六进制:

text 复制代码
250 / 16 = 15 ... 10
 15 / 16 =  0 ... 15

余数 10 写作 A,余数 15 写作 F,逆序得到:

text 复制代码
(250)_10 = (FA)_16

5. 十进制 0 是特殊情况

如果算法一开始就判断"商为 0"并直接结束,可能得不到任何数码。数学上:

text 复制代码
(0)_10 = (0)_r

程序实现时必须单独处理输入为 0 的情况。

八、十进制小数转其他进制:乘基取整法

对 0 <= F < 1 的十进制小数,转换到 r 进制:

  1. 用小数部分乘以 r;
  2. 取乘积的整数部分,作为结果当前最高位;
  3. 保留乘积的小数部分,再乘以 r;
  4. 小数部分变为 0 时结束,或达到规定精度时停止;
  5. 整数部分按产生顺序从前向后排列,不需要倒序。

1. 十进制 0.625 转二进制

text 复制代码
0.625 x 2 = 1.250  -> 取整数 1,保留 0.250
0.250 x 2 = 0.500  -> 取整数 0,保留 0.500
0.500 x 2 = 1.000  -> 取整数 1,保留 0

所以:

text 复制代码
(0.625)_10 = (0.101)_2

验证:

text 复制代码
1 x 2^-1 + 0 x 2^-2 + 1 x 2^-3
= 1/2 + 1/8
= 0.625

2. 十进制 0.6875 转十六进制

text 复制代码
0.6875 x 16 = 11.0

整数部分 11 对应十六进制 B,小数部分已经为 0:

text 复制代码
(0.6875)_10 = (0.B)_16

3. 整数与小数分别转换

转换十进制 86.625 到二进制时:

  • 整数部分 86 使用除 2 取余,得到 1010110;
  • 小数部分 0.625 使用乘 2 取整,得到 .101;
  • 最后在小数点处合并。

结果是:

text 复制代码
(86.625)_10 = (1010110.101)_2

口诀可以概括为:

text 复制代码
整数:除基取余,余数倒排
小数:乘基取整,整数顺排

九、为什么有些小数无法有限表示

十进制 0.1 看起来很简单,但转为二进制时会无限循环:

text 复制代码
(0.1)_10 = (0.00011001100110011...)_2

这是进制表示的性质,不是转换算法出错。类似地,十进制中的 1/3 只能写成循环小数 0.333...。

1. 有限表示的判断条件

把一个有理数的小数部分写成最简分数 p/q。它能在基数 r 中有限表示,当且仅当分母 q 的所有质因数都包含在基数 r 的质因数中。

  • 十进制基数 10 = 2 x 5,所以最简分母只含 2 和 5 时可以有限表示;
  • 二进制基数 2,所以最简分母必须只含因数 2;
  • 八进制 8 = 2^3,条件与二进制相同;
  • 十六进制 16 = 2^4,条件也与二进制相同。

0.1 = 1/10 的分母包含因数 5,所以无法用有限二进制位精确表示;0.625 = 5/8 的分母只有因数 2,因此可以有限表示。

2. 截断与舍入

如果只保留 0.1 的前 12 个二进制小数位:

text 复制代码
(0.000110011001)_2 = (0.099853515625)_10

它比真实的 0.1 略小。根据下一位进行舍入后可能得到:

text 复制代码
(0.000110011010)_2 = (0.10009765625)_10

它又比真实的 0.1 略大。计算机浮点数只能使用有限位数,因此通常保存最接近的可表示值。这就是很多浮点计算出现微小误差的根源。

十、如何设置小数转换精度

乘基取整法必须有明确的停止条件:

  1. 小数部分准确变为 0;
  2. 已生成规定数量的目标进制小数位;
  3. 余数状态重复,可以确认进入循环。

如果要求保留 n 位目标进制小数,应至少再计算一位,用于决定是否舍入。例如保留 4 位二进制小数,需要观察第 5 位。

目标进制中保留 n 位后,相邻可表示数之间的间隔是:

text 复制代码
r^-n

直接截断的绝对误差小于这个间隔;按最接近值舍入时,误差通常不超过半个间隔,但具体边界还取决于舍入规则。

十一、负数怎样做数学进制转换

如果讨论的是数学上的进制表示,可以先转换绝对值,再保留负号。例如:

text 复制代码
(-86)_10 = -(86)_10 = (-1010110)_2

这里的负号是数学符号,不占数码位。

计算机内存中通常不会额外保存一个字符 -,而是用固定宽度的补码表示有符号整数。例如 -86 的 8 位、16 位和 32 位补码写法不同。补码属于"固定宽度下的数据编码",不能与普通的负号表示混为一谈,将在下一篇详细说明。

十二、任意进制之间的通用转换

将 p 进制转为 q 进制,通用路线是:

text 复制代码
p 进制 --位权展开--> 十进制 --除基/乘基--> q 进制

例如把 (10201)_3 转为五进制。

先转十进制:

text 复制代码
(10201)_3
= 1 x 3^4 + 0 x 3^3 + 2 x 3^2 + 0 x 3^1 + 1 x 3^0
= 81 + 18 + 1
= 100

再把 100 转为五进制:

text 复制代码
100 / 5 = 20 ... 0
 20 / 5 =  4 ... 0
  4 / 5 =  0 ... 4

逆序读取:

text 复制代码
(10201)_3 = (100)_10 = (400)_5

这种方法适用于任意合理基数。对于二、八、十六进制,由于基数之间是 2 的幂关系,可以直接按 3 位或 4 位二进制分组,下一篇会系统介绍这种更快的方法。

十三、转换结果如何自检

1. 反向位权展开

得到目标进制结果后,重新展开为十进制,看是否回到原值。例如:

text 复制代码
(126)_8 = 1 x 64 + 2 x 8 + 6 = 86

2. 检查数码是否合法

目标是八进制却出现 8,目标是二进制却出现 2,说明结果一定错误。

3. 检查数量级

因为:

text 复制代码
2^6 = 64 <= 86 < 128 = 2^7

所以 86 的二进制应有 7 个有效整数位。若结果只有 5 位或有 10 位,应重新检查。

4. 检查最低位

二进制整数的最低位可以反映奇偶性:

  • 偶数最低位是 0;
  • 奇数最低位是 1。

86 是偶数,因此结果 1010110 以 0 结尾,符合预期。

5. 保留精确分数

处理小数时,尽可能用分数验证。例如:

text 复制代码
(0.101)_2 = 1/2 + 1/8 = 5/8 = 0.625

分数不会引入十进制浮点近似,更适合检查转换过程。

十四、常见错误

1. 把余数按原顺序写出

除基取余第一次得到最低位,必须逆序排列。乘基取整第一次得到最高小数位,必须顺序排列。两种算法方向恰好相反。

2. 位权从 1 次方开始

小数点左侧最靠近小数点的一位是 r^0,不是 r^1。

3. 忘记十六进制字母的数值

A~F 不是变量,分别代表 10~15。(10)_16 是十进制 16,不是十进制 10。

4. 把小数点后的数码当十进制小数

(0.1)_2 表示 1 x 2^-1 = 0.5,不是十进制 0.1;(0.1)_16 表示 1/16 = 0.0625。

5. 认为所有小数都能转换到"余数为 0"

某些小数在目标进制中无限循环,必须按精度停止并说明是近似值。

6. 转换过程中太早使用近似十进制

任意进制小数经过十进制中转时,如果提前舍入,最终结果可能继续放大误差。能用精确分数或直接分组时,应优先保留精确形式。

十五、总结

所有位置进制都遵循同一条规律:每一位的实际贡献等于"数码乘以基数的对应次幂"。因此,其他进制转十进制使用位权展开;十进制整数转其他进制使用除基取余并逆序;十进制小数转其他进制使用乘基取整并顺序记录。

真正需要理解的难点有两个:第一,整数和小数两种转换算法的记录方向不同;第二,有限十进制小数不一定能用有限二进制小数表示。掌握这些原理后,不仅能手工转换,也能理解计算机浮点误差、整数编码和 C 语言进制输出的基础。

相关推荐
HEJOO92 小时前
Linux 设备驱动系列——使用全局工作队列
java·linux·算法
顺风尿一寸3 小时前
手写迷你 Docker:用 Namespace + Cgroup v2 实现一个简单容器
linux
一木 之林3 小时前
DeepSeek Agent 开发(三)
java·大数据·linux
大猫会长4 小时前
调用codebuddy集成的supabase授权白屏解决方法
linux·运维·服务器
羔羊++4 小时前
29_实验二十八_buildroot配置与编译
linux
LoneEon5 小时前
CentOS7 部署 Nacos3.x 集群实战:从注册中心到 AI 管理中心
linux·人工智能·nacos
传人5 小时前
为什么 Xshell 中可以通过java -version查询到java 版本号码,Electerm 却查不到?
java·linux·服务器
鬼手点金6 小时前
opencode-全能开发者配置
java·linux·服务器·前端·javascript·学习·前向传播
xixiaoyunya6 小时前
虚拟机备份怎么做:镜像级与文件级搭配的完整实操方案
linux·运维·网络