进位计数法和不同进制数相互转换
进位计数制
一般我们常用的计数进位制有
- 十进制:172
- 二进制:10101100
- 八进制:254
- 十六进制:AC
例如:
69
=
6
∗
10
+
9
∗
1
=
6
∗
1
0
1
+
9
∗
1
0
0
69=6*10+9*1=6*10^1+9*10^0
69=6∗10+9∗1=6∗101+9∗100
这里的
10
10
10就是基(base),那么我们可以做一步抽象得到:
设
b
a
s
e
为
k
x
=
a
n
∗
k
n
+
⋯
+
a
1
∗
k
1
+
a
0
∗
k
0
=
∑
i
=
0
n
a
i
∗
k
i
设base为k\\ x = a_n*k^n+\cdots+a_1*k^1+a_0*k^0=\textcolor{blue}{\sum_{i=0}^na_i*k^i}
设base为kx=an∗kn+⋯+a1∗k1+a0∗k0=i=0∑nai∗ki
当且讨论都是在整数范围,如果我们要表示一个小数呢?
不妨继续按照刚才思路进一步抽象
x
=
a
n
∗
k
n
+
⋯
+
a
1
∗
k
1
+
a
0
∗
k
0
+
a
−
1
∗
k
−
1
+
a
−
2
∗
k
−
2
+
⋯
x
=
∑
i
=
−
∞
n
a
i
∗
k
i
x=a_n*k^n+\cdots+a_1*k^1+a_0*k^0+\textcolor{red}{a_{-1}*k^{-1}+a_{-2}*k^{-2}+\cdots}\\ \textcolor{blue}{x=\sum_{i=-\infty}^{n}a_i*k^i}
x=an∗kn+⋯+a1∗k1+a0∗k0+a−1∗k−1+a−2∗k−2+⋯x=i=−∞∑nai∗ki
任意进制数到十进制的转换方法
通过观察我们发现上述和式就是一个把任意进制数转化为十进制的通用公式。
例如:
0.
A
8
(
16
)
=
0
∗
1
6
0
+
10
∗
1
6
−
1
+
8
∗
1
6
−
2
=
0.6262
5
(
10
)
34.
5
(
8
)
=
3
∗
8
1
+
4
∗
8
0
+
5
∗
8
−
1
=
28.62
5
(
10
)
0.A8_{(16)}=0*16^0+10*16^{-1}+8*16^{-2}=0.62625_{(10)}\\ 34.5_{(8)}=3*8^1+4*8^0+5*8^{-1}=28.625_{(10)}
0.A8(16)=0∗160+10∗16−1+8∗16−2=0.62625(10)34.5(8)=3∗81+4∗80+5∗8−1=28.625(10)
数字右下脚标代表进制。
十进制转换为其它进制数
还是观察那个和式,我们发现对于 k k k进制数,如果能够找到每个 k i k^i ki前的系数 a i a_i ai,那么我们也就找到了这个数的 k k k进制表示。
下来就要分成整数部分和小数部分来处理。
-
整数部分,把 x x x的整数部分对 k k k取余数得到 a 0 a_0 a0,让 x x x除以 k k k,继续进行上述操作,直到 x x x的整数部分为0: a 0 = x % k , a 1 = ( x / k ) % k , ⋯ a_0=x\%k,a_1=(x/k)\%k,\cdots a0=x%k,a1=(x/k)%k,⋯
- 例:
73.548 的 整 数 部 分 转 为 8 进 制 x = 73 a 0 = x % 8 = 1 , x = ⌊ x 8 ⌋ = 9 a 1 = x % 8 = 1 , x = ⌊ x 8 ⌋ = 1 a 2 = x % 8 = 1 , x = ⌊ x 8 ⌋ = 0 综 上 7 3 ( 10 ) = 1 ∗ 8 2 + 1 ∗ 8 1 + 1 ∗ 8 0 = 11 1 ( 8 ) 73.548的整数部分转为8进制\\ x = 73\\ a_0 = x \% 8 = 1,~ x=\lfloor\frac{x}{8}\rfloor= 9\\ a_1 = x \% 8 = 1,~ x=\lfloor\frac{x}{8}\rfloor= 1\\ a_2 = x \% 8 = 1,~ x=\lfloor\frac{x}{8}\rfloor= 0\\ 综上73_{(10)}=1*8^2+1*8^1+1*8^0=111_{(8)} 73.548的整数部分转为8进制x=73a0=x%8=1, x=⌊8x⌋=9a1=x%8=1, x=⌊8x⌋=1a2=x%8=1, x=⌊8x⌋=0综上73(10)=1∗82+1∗81+1∗80=111(8)
- 例:
-
小数部分,让 x x x的小数部分乘以 k k k的取整到 a − 1 a_{-1} a−1, 再对该乘后的结果的小数部分执行同样的操作得到 a − 2 ⋯ a_{-2}\cdots a−2⋯,重复执行。
- 例:
73.548 的 小 数 部 分 转 为 8 进 制 x = 0.548 a − 1 = ⌊ x ∗ 8 ⌋ = ⌊ 4.384 ⌋ = 4 , x = x ∗ 8 ( 取 小 数 部 分 ) = 0.384 a − 2 = ⌊ x ∗ 8 ⌋ = ⌊ 3.072 ⌋ = 3 , x = x ∗ 8 ( 取 小 数 部 分 ) = 0.072 a − 3 = ⌊ x ∗ 8 ⌋ = ⌊ 0.576 ⌋ = 0 , x = x ∗ 8 ( 取 小 数 部 分 ) = 0.576 a − 4 = ⌊ x ∗ 8 ⌋ = ⌊ 4.608 ⌋ = 4 , x = x ∗ 8 ( 取 小 数 部 分 ) = 0.608 ⋮ 73.548的小数部分转为8进制\\ x=0.548\\ a_{-1}=\lfloor x*8\rfloor=\lfloor4.384\rfloor=4,~x=x*8(取小数部分)=0.384\\ a_{-2}=\lfloor x*8\rfloor=\lfloor3.072\rfloor=3,~x=x*8(取小数部分)=0.072\\ a_{-3}=\lfloor x*8\rfloor=\lfloor0.576\rfloor=0,~x=x*8(取小数部分)=0.576\\ a_{-4}=\lfloor x*8\rfloor=\lfloor4.608\rfloor=4,~x=x*8(取小数部分)=0.608\\ \vdots 73.548的小数部分转为8进制x=0.548a−1=⌊x∗8⌋=⌊4.384⌋=4, x=x∗8(取小数部分)=0.384a−2=⌊x∗8⌋=⌊3.072⌋=3, x=x∗8(取小数部分)=0.072a−3=⌊x∗8⌋=⌊0.576⌋=0, x=x∗8(取小数部分)=0.576a−4=⌊x∗8⌋=⌊4.608⌋=4, x=x∗8(取小数部分)=0.608⋮
我们发现在有些情况下小数有可能出现无限位的情况,这是正常的,这是进制本身的一种特性。
- 例:
-
综上 73.54 8 ( 10 ) = 111.4304 ⋯ ( 8 ) 73.548_{(10)}=111.4304\cdots_{(8)} 73.548(10)=111.4304⋯(8)
快速的二进制,八进制,十六进制转化
首先,我们显然可将二进制先转为十进制,然后再用十进制转为八进制或十六进制。
这里给出一种更好的做法:
思考一下,8进制下,一位能有几个数字,8个 ( 0 , ⋯ , 7 ) (0,\cdots,7) (0,⋯,7),那么要用二进制来表示,其实也就是2的多少次方能表示这8个数字, 2 x = 8 ⇒ x = log 2 8 = 3 2^x=8 \Rightarrow x = \log_28=3 2x=8⇒x=log28=3,也就是说我们只需用3个二进制位就可以表示8进制下的一位。
例如:
11010100
1
(
2
)
=
110
101
001
(
拆
成
3
位
一
组
)
其
中
110
(
2
)
=
6
(
8
)
,
101
(
2
)
=
5
(
8
)
,
001
(
2
)
=
1
(
8
)
所
以
:
110
101
001
(
2
)
=
65
1
(
8
)
110101001_{(2)}=\textcolor{red}{110}~\textcolor{green}{101}~\textcolor{blue}{001}(拆成3位一组)\\ 其中 \textcolor{red}{110}_{(2)}=6_{(8)},~\textcolor{green}{101}_{(2)}=5_{(8)}, \textcolor{blue}{001}_{(2)}=1_{(8)}\\ 所以:\textcolor{red}{110}\textcolor{green}{101}\textcolor{blue}{001}_{(2)} = 651_{(8)}
110101001(2)=110 101 001(拆成3位一组)其中110(2)=6(8), 101(2)=5(8),001(2)=1(8)所以:110101001(2)=651(8)
十六进制也是同理,只需将二进制拆成四个一组即可,因为
2
4
=
16
2^4=16
24=16。
部分代码实现
- 十进制转[2,16]之间的进制
// c++实现
/**
* 该函数将10进制数转化为[2,16]进制数字的字符串表示, O(log(num))
* @param num 10进制数字
* @param k 转为k进制 2 <= k <= 16
* @return k进制下数字字符串
*/
string dec2arbitrary(double num, int k) {
assert(k >= 2 && k <= 16);
char mp[] = {'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'};
string ret;
// 处理整数部分
{
long n = (long) num;
while (n > 0) {
ret.push_back(mp[n % k]);
n /= k;
}
std::reverse(ret.begin(), ret.end());
ret.push_back('.');
}
// 处理小数部分
{
double x = num - (long) num;
double dif = 1e-4; // 判断与0的接近程度
// 由于小数部分有时有无穷位,所以我们一般要求精确到某位
int bits = 10;
for (int i = 0; i < bits && x > dif; i++) {
x *= k;
ret.push_back(mp[(long) x]);
x -= (long) x;
}
}
return ret;
}
习题
- 尝试用和式的形式表示 768.445 768.445 768.445。
- 尝试将 3 F 3 F 转 为 10 进 制 。 3F3F转为10进制。 3F3F转为10进制。
- 尝试将 100101 转 为 10 进 制 。 100101转为10进制。 100101转为10进制。
- 尝试将 7562.03125 转 为 8 进 制 。 7562.03125转为8进制。 7562.03125转为8进制。
- 尝试将 7562.03125 转 为 16 进 制 。 7562.03125转为16进制。 7562.03125转为16进制。
- 尝试将 10111011 转 为 8 进 制 。 10111011转为8进制。 10111011转为8进制。
- 尝试将 10111011 转 为 16 进 制 。 10111011转为16进制。 10111011转为16进制。