记数系统是一套用于表示和处理数字的符号规则体系,包括数字符号和记数规则。不同的文明和应用场景发展出了不同的记数系统。
数字符号有非常多的种类,数学和计算机中常用阿拉伯数字来进行记数。
其他常见数字符号如下:
进位制是日常最常用的记数规则。n 进制就是使用 n 种数字符号来记数,满 n 进 1。
常见的进位制包括:
除此之外,很多单位转换也利用了 n 进制的思想,例如:
十进制转 n 进制分为整数部分的转换和小数部分的转换。十进制转 n 进制的过程与十进制求每一位上的数是什么的过程极为相似,如果不记得具体的求解方式,可以通过考虑如何获得十进制各个位上的数来梳理思路。
方法:除基取余,逆序排列
将十进制整数转换为 n 进制整数的步骤:
示例1:将十进制数 456 转为二进制
从右到左依次求各位的值:
| 步骤 | 运算 | 余数 | 商 |
|---|---|---|---|
| 第1位 | 456 % 2 | 0 | 228 |
| 第2位 | 228 % 2 | 0 | 114 |
| 第3位 | 114 % 2 | 0 | 57 |
| 第4位 | 57 % 2 | 1 | 28 |
| 第5位 | 28 % 2 | 0 | 14 |
| 第6位 | 14 % 2 | 0 | 7 |
| 第7位 | 7 % 2 | 1 | 3 |
| 第8位 | 3 % 2 | 1 | 1 |
| 第9位 | 1 % 2 | 1 | 0 |
将余数从下往上排列,得到二进制整数为:111001000
示例2:将十进制数 100 转为八进制
| 步骤 | 运算 | 余数 | 商 |
|---|---|---|---|
| 第1步 | 100 / 8 = 12 余 4 | 4 | 12 |
| 第2步 | 12 / 8 = 1 余 4 | 4 | 1 |
| 第3步 | 1 / 8 = 0 余 1 | 1 | 0(结束) |
将余数从下往上排列,得到八进制数:144
验证:1 \ 8^2 + 4 \ 8^1 + 4 \* 8^0 = 64 + 32 + 4 = 100 ✓
示例3:将十进制数 255 分别转为二进制、八进制、十六进制
验证:11111111(2) = 377(8) = FF(16) = 255(10) ✓
方法:乘基取整,正序排列
将十进制小数转化为 n 进制小数的步骤:
注意:整数部分转换是"逆序",小数部分转换是"正序",两者方向相反,不要混淆!
示例1:将十进制数 0.625 转为二进制
从左到右依次求各位的值:
| 步骤 | 运算 | 整数部分 | 剩余小数 |
|---|---|---|---|
| 第1位 | 0.625 \* 2 = 1.25 | 1 | 0.25 |
| 第2位 | 0.25 \* 2 = 0.5 | 0 | 0.5 |
| 第3位 | 0.5 \* 2 = 1.0 | 1 | 0 |
二进制小数为:0.101
验证:1 \ 2^(-1) + 0 \ 2^(-2) + 1 \* 2^(-3) = 0.5 + 0 + 0.125 = 0.625 ✓
示例2:将十进制数 0.7 转为二进制(无限循环小数)
| 步骤 | 运算 | 整数部分 | 剩余小数 |
|---|---|---|---|
| 第1位 | 0.7 \* 2 = 1.4 | 1 | 0.4 |
| 第2位 | 0.4 \* 2 = 0.8 | 0 | 0.8 |
| 第3位 | 0.8 \* 2 = 1.6 | 1 | 0.6 |
| 第4位 | 0.6 \* 2 = 1.2 | 1 | 0.2 |
| 第5位 | 0.2 \* 2 = 0.4 | 0 | 0.4 |
| ... | 开始循环 | ... | ... |
二进制小数为:0.10110...(无限循环)
提示:并非所有十进制小数都能精确转换为 n 进制小数,可能产生无限循环小数。这也是计算机中浮点数精度问题的根源之一。
下面的代码实现了十进制整数转任意 n 进制的功能:
1#include <bits/stdc++.h>
2using namespace std;
3
4string decimal2base(int decimal_number, int base) {
5 if (decimal_number == 0) {
6 return "0";
7 }
8 string base_number;
9 while (decimal_number) {
10 int value = decimal_number % base;
11 char digit;
12 if (value < 10) {
13 digit = value + '0';
14 } else {
15 digit = value - 10 + 'A';
16 }
17 base_number += digit;
18 decimal_number /= base;
19 }
20 int len = base_number.length();
21 for (int i = 0; i < len / 2; ++i) {
22 swap(base_number[i], base_number[len - 1 - i]);
23 }
24 return base_number;
25}
26
27int main() {
28 int n;
29 cin >> n;
30 for (int i = 0; i < n; ++i) {
31 int base, decimal_number;
32 cin >> base >> decimal_number;
33 string base_number = decimal2base(decimal_number, base);
34 cout << base_number << endl;
35 }
36 return 0;
37}n 进制转十进制只需要把 n 进制每一位的数值乘以该位的权重即可。权重就是 n 的对应次幂,类似于已知十进制数上的每一位求十进制数。
方法:按权展开,逐位求和
将 n 进制整数转换为十进制整数的步骤:
示例1:二进制数 111001000 转十进制
| 位置(从右) | 数字 | 数值 | 权重 | 计算 |
|---|---|---|---|---|
| 第8位 | 1 | 1 | 2^8 = 256 | 1 \* 256 = 256 |
| 第7位 | 1 | 1 | 2^7 = 128 | 1 \* 128 = 128 |
| 第6位 | 1 | 1 | 2^6 = 64 | 1 \* 64 = 64 |
| 第5位 | 0 | 0 | 2^5 = 32 | 0 \* 32 = 0 |
| 第4位 | 0 | 0 | 2^4 = 16 | 0 \* 16 = 0 |
| 第3位 | 1 | 1 | 2^3 = 8 | 1 \* 8 = 8 |
| 第2位 | 0 | 0 | 2^2 = 4 | 0 \* 4 = 0 |
| 第1位 | 0 | 0 | 2^1 = 2 | 0 \* 2 = 0 |
| 第0位 | 0 | 0 | 2^0 = 1 | 0 \* 1 = 0 |
结果:256 + 128 + 64 + 8 = 456 ✓
示例2:十六进制数 2A3 转十进制
| 位置(从右) | 数字 | 数值 | 权重 | 计算 |
|---|---|---|---|---|
| 第2位 | 2 | 2 | 16^2 = 256 | 2 \* 256 = 512 |
| 第1位 | A | 10 | 16^1 = 16 | 10 \* 16 = 160 |
| 第0位 | 3 | 3 | 16^0 = 1 | 3 \* 1 = 3 |
结果:512 + 160 + 3 = 675 ✓
方法:按权展开,逐位求和(负幂次)
将 n 进制小数转换为十进制小数的步骤:
示例:二进制小数 0.101 转十进制
| 位置(小数点后) | 数字 | 数值 | 权重 | 计算 |
|---|---|---|---|---|
| 第1位 | 1 | 1 | 2^(-1) = 0.5 | 1 \* 0.5 = 0.5 |
| 第2位 | 0 | 0 | 2^(-2) = 0.25 | 0 \* 0.25 = 0 |
| 第3位 | 1 | 1 | 2^(-3) = 0.125 | 1 \* 0.125 = 0.125 |
结果:0.5 + 0 + 0.125 = 0.625 ✓
在代码实现中,n 进制转十进制通常不需要逐位计算幂次,而是使用更高效的秦九韶算法。
核心公式:decimalnumber = decimalnumber * base + value
以十六进制 "2A3" 转十进制为例,跟踪执行过程:
| 步骤 | 当前字符 | value | decimal_number 计算过程 | decimal_number |
|---|---|---|---|---|
| 初始 | - | - | - | 0 |
| i=0 | '2' | 2 | 0 \* 16 + 2 | 2 |
| i=1 | 'A' | 10 | 2 \* 16 + 10 | 42 |
| i=2 | '3' | 3 | 42 \* 16 + 3 | 675 |
这种方法从高位到低位逐步累积,避免了计算幂次,非常巧妙高效。
原理说明: 以 "2A3" 为例,按权展开是 2 \ 16^2 + 10 \ 16^1 + 3 \ 16^0,通过提取公因式变形为 ((2) \ 16 + 10) \* 16 + 3,这正是代码的计算顺序。
下面的代码实现了任意 n 进制整数转十进制的功能:
1#include <bits/stdc++.h>
2using namespace std;
3
4int base2decimal(string &base_number, int base) {
5 int decimal_number = 0;
6 int len = base_number.length();
7 for (int i = 0; i < len; ++i) {
8 char digit = base_number[i];
9 int value;
10 if (digit >= '0' && digit <= '9') {
11 value = digit - '0';
12 }
13 if (digit >= 'A' && digit <='Z') {
14 value = digit - 'A' + 10;
15 }
16 decimal_number = decimal_number * base + value;
17 }
18 return decimal_number;
19}
20
21int main() {
22 int n;
23 cin >> n;
24 for (int i = 0; i < n; ++i) {
25 int base;
26 string base_number;
27 cin >> base >> base_number;
28 int decimal_number = base2decimal(base_number, base);
29 cout << decimal_number << endl;
30 }
31 return 0;
32}value - 10 + 'A' 写错