本文主要介绍进制、进制转换、原码反码补码的知识。
文章目录
1. 进制
本章主要介绍什么是进制。现有数字 6179,从右到左分别为个位、十位、百位、千位,这是我们小学的知识了(所以不用害怕知识有多难,我们小学就接触了)。
6179 就是一个十进制数,而我们在平时的生活中接触到的也是十进制数。现在我们来说说十进制数的特点:
- 从右到左,分别为个位、十位、百位、千位、万位…;
- 每一位上的数字可以为0-9之间的任意一个数字;
- 逢十进一;
在编程世界中,最基础的是二进制,那么什么是二进制呢?类比十进制,二进制有如下特点:
- 我们也可以将二进制的数从右到左分为每一位;
- 每一位上的数字要么是0,要么是1;
- 逢二进一;
例如,二进制下的数字 10010。
除了二进制,我们还常用八进制、十六进制。再次总结一下进制的知识:
- 对于n进制,每一位上的数字可以为
[0,n-1]
; - 对于n进制,低位的数字逢n进一。
以十六进制为例,每一位上的数字可以为[0,15]
,例如:
可是,这种表示方法有歧义,我们用一个方框表示一位,可是在实际使用中是没有这个方框的,那么就会显示如下图,可以有两种以上的理解:
这就造成了歧义,原因是[10,15]的数字我们用了两位来表示,为了消除歧义,我们用A-F分别表示10-15:
所以十六进制的每一位上的数字可以为[0,15],但是为了消除歧义,10-15分别用A-F来表示。
2. 进制转换
本节主要介绍同一个整数不同进制之间的转换。对于小数的进制转换,感兴趣的同学可以自行查阅资料。我们以角标 X ( n ) X_{(n)} X(n)表示X是n进制数。
2.1 十进制转换为其它进制
我们分别以二进制、八进制为例,介绍十进制是如何转换为其它进制的。
其实十进制转换为其他进制所采用的方法是一样的,假设十进制转换为n进制,那么该方法可以称为“除n取余,逆序排列”。更准确地描述为,现有一个十进制数x,将其转换为n进制数,则转换方法为:
- 首先计算 x ÷ n = p … q,得到商为p,余数为 q;
- 再根据上一步得到的商 p 继续同样的步骤 p ÷ n = p 1 . . . q 1 p_1 ... q_1 p1...q1,得到商为 p 1 p_1 p1,余数为 q 1 q_1 q1;
- 直到商为0;
- 最后将余数逆序从左至右排列 p n p n − 1 . . . p 1 p p_np_{n-1}...p_1p pnpn−1...p1p,则 p n p n − 1 . . . p 1 p p_np_{n-1}...p_1p pnpn−1...p1p就是转换后的n进制数;
以十进制数
10
0
(
10
)
100_{(10)}
100(10)转换为二进制数为例:
第
一
步
:
100
÷
2
=
50...0
第
二
步
:
50
÷
2
=
25...0
第
三
步
:
25
÷
2
=
12...1
第
四
步
:
12
÷
2
=
6...0
第
五
步
:
6
÷
2
=
3...0
第
六
步
:
3
÷
2
=
1...1
第
七
步
:
1
÷
2
=
0...1
第一步: 100 \div 2 = 50 ... 0 \\ 第二步: 50 \div 2 = 25 ... 0 \\ 第三步: 25 \div 2 = 12 ... 1 \\ 第四步: 12 \div 2 = 6 ... 0 \\ 第五步: 6 \div 2 = 3 ... 0 \\ 第六步: 3 \div 2 = 1 ... 1 \\ 第七步: 1 \div 2 = 0 ... 1 \\
第一步:100÷2=50...0第二步:50÷2=25...0第三步:25÷2=12...1第四步:12÷2=6...0第五步:6÷2=3...0第六步:3÷2=1...1第七步:1÷2=0...1
最后将余数逆序排列,得到
10
0
(
10
)
100_{(10)}
100(10)的二进制形式为
110010
0
(
2
)
1100100_{(2)}
1100100(2)。
在实际的转换中,我们可以按照如下的写法进行简化:
同理,十进制数 10 0 ( 10 ) 100_{(10)} 100(10)转换为八进制数的步骤如下:
即十进制数 10 0 ( 10 ) 100_{(10)} 100(10)转换为八进制数为 14 4 ( 8 ) 144_{(8)} 144(8)。
2.2 其它进制转换为十进制
现有一个n进制数
p
x
−
1
.
.
.
p
1
p
0
p_{x-1}...p_1p_0
px−1...p1p0,共有x位,则将其转换位十进制数的公式为:
例如,现有二进制数 1110110 0 ( 2 ) 11101100_{(2)} 11101100(2),转换为十进制数如下:
2.3 二进制、八进制、十六进制之间的转换
由于二进制、八进制和十六进制之间存在特殊的对应关系,所以二进制和八进制、十六进制之间的互相转换存在简便方法。
首先,我们先确定三位二进制数的对应关系:
000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 |
---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
三位二进制数所对应的十进制数为0—7,这就是八进制中的八个数字。
二进制数转换为八进制数的方法如下:对于二进制数,从右到左,以三位二进制数为一组,若最后一组不足三位,则高位补零。然后依次将每组的二进制数按照对应关系转换为八进制数,得到的结果就是八进制数。
例如,现有二进制数 1100110101001 0 ( 2 ) 11001101010010_{(2)} 11001101010010(2),将其转换为八进制数的步骤如下:
首先从右到左以三位二进制数为一组:
11
001
101
010
010
11 \quad 001 \quad 101 \quad 010 \quad 010
11001101010010
可以看到最左边的一组(最后一组)不足三位,则最高位补零:
011
001
101
010
010
011 \quad 001 \quad 101 \quad 010 \quad 010
011001101010010
然后按照对应关系,将每组转换为八进制数:
011
001
101
010
010
=
3
1
5
2
2
=
3152
2
(
8
)
011 \quad 001 \quad 101 \quad 010 \quad 010 = 3\quad 1\quad 5 \quad 2\quad 2 = 31522_{(8)}
011001101010010=31522=31522(8)
即二进制数
1100110101001
0
(
2
)
11001101010010_{(2)}
11001101010010(2)转换为八进制数为
3152
2
(
8
)
31522_{(8)}
31522(8)。
同理,二进制数转换为十六进制数,就是四位二进制数与十六进制数的对应关系:
0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 |
---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
8 | 9 | A | B | C | D | E | F |
转换方法类似:对于二进制数,从右到左,以四位二进制数为一组,若最后一组不足四位,则高位补零。然后依次将每组的二进制数按照对应关系转换为十六进制数,得到的结果就是十六进制数。
例如,现有二进制数 1110111101001 0 ( 2 ) 11101111010010_{(2)} 11101111010010(2),将其转换为十六进制数的步骤如下:
首先从右到左以四位二进制数为一组:
11
1011
1101
0010
11 \quad 1011 \quad 1101 \quad 0010
11101111010010
可以看到最左边的一组(最后一组)不足四位,则最高位补零:
0011
1011
1101
0010
0011 \quad 1011 \quad 1101 \quad 0010
0011101111010010
然后按照对应关系,将每组转换为十六进制数:
0011
1011
1101
0010
=
3
B
D
2
=
3
B
D
2
(
16
)
0011 \quad 1011 \quad 1101 \quad 0010 = 3 \quad B \quad D \quad2 = 3BD2_{(16)}
0011101111010010=3BD2=3BD2(16)
即二进制数
1110111101001
0
(
2
)
11101111010010_{(2)}
11101111010010(2)转换为十六进制数为
3
B
D
2
(
16
)
3BD2_{(16)}
3BD2(16)。
3. 二进制中的存储单位
对于二进制中的一位,我们称之为比特位(bit),即一位=1bit,在此基础上,有如下单位:
- 1字节=8位,即1byte=8bit,缩写为1B = 8b
- 1千字节 = 1024字节,即 1 K B = 2 10 B 1KB = 2^{10}B 1KB=210B
- 1兆字节 = 1024千字节,即 1 M B = 2 20 B 1 MB = 2^{20}B 1MB=220B
- 1吉字节 = 1024兆字节,即 1 G B = 2 30 B 1GB = 2^{30}B 1GB=230B
- 1太字节 = 1024吉字节,即 1 T B = 1024 G B = 2 40 B 1 TB = 1024GB = 2^{40}B 1TB=1024GB=240B
还有更高的单位,但是平时也涉及不到,因此此处只罗列常见的。
4. 原码、反码、补码
4.1 原码、反码和补码
本文介绍整数的几种编码方式。
**原码:**原码将数字的二进制分为两部分,符号位和数字的绝对值二进制表示;对于正数来说,符号位为0;对于负数来说,符号位为1。例如,用8位二进制位来存储整数,10和-10的原码表示如下:
10: 0000 1010
-10: 1000 1010
反码:对于正数来说,反码就是原码;对于负数来说,反码就是在原码的基础上,符号位不变,其余位反转。例如,10和-10的反码如下:
10: 0000 1010
-10: 1111 0101
补码:对于正数来说,补码就是原码;对于负数来说,补码就是在反码的基础上,加1。例如,10和-10的补码如下:
10: 0000 1010
-10: 1111 0110
在C语言中,存储整数都是按照补码的形式来存储的。
从十进制整数到二进制补码的转换我们已经了解了,那么如何从补码快速得到十进制整数呢?
对
于
一
个
n
位
的
二
进
制
补
码
{
x
n
−
1
,
x
n
−
2
.
.
.
x
1
,
x
0
}
来
说
,
将
其
转
换
为
对
应
的
十
进
制
整
数
方
法
如
下
:
{
x
n
−
1
,
x
n
−
2
.
.
.
x
1
,
x
0
}
=
−
x
n
−
1
∗
2
n
−
1
+
∑
k
=
0
n
−
2
x
k
2
k
对于一个n位的二进制补码\{x_{n-1}, x_{n-2} ... x_1, x_0\}来说,将其转换为对应的十进制整数方法如下:\\ \{x_{n-1}, x_{n-2} ... x_1, x_0\} = -x_{n-1}*2^{n-1} + \sum_{k = 0}^{n-2}x^{k}2^k
对于一个n位的二进制补码{xn−1,xn−2...x1,x0}来说,将其转换为对应的十进制整数方法如下:{xn−1,xn−2...x1,x0}=−xn−1∗2n−1+k=0∑n−2xk2k
4.2 为什么用补码存储整数?
首先我们来看原码的不足之处:
-
原码计算涉及负数的减法存在问题。例如,计算 1 - 1,1 - 1 也可以看成 1 + ( -1 ) ,则转换为原码(以8位二进制数来存储整数)为:
1 + (-1) = 00000001 + 10000001 = 10000010 = -2
这样的计算结果显然是不对的。
-
在原码表示中,0有两种表示方式。
00000000: +0 10000000: -0
在原码表示中,0分为了+0和-0,这显然是不符合我们的习惯的,0是不分正负的。
再来看看反码有何不足之处:
-
反码对于两个负数的加法计算存在问题。例如,计算 - 1 + (-1):
-1 + (-1) = 11111110 + 11111110 = 1 00000000 = 00000000 = 0
由于只用8位二进制位来存储数据,所以最高位的1省去。最后结果为0,结果显然不对。
-
在反码表示中,0仍然有两种表示方式。
00000000: +0 11111111: -0
为了解决原码和反码存在的问题,提出了补码。
参考资料
[1] 进制:https://zh.wikipedia.org/wiki/%E8%BF%9B%E4%BD%8D%E5%88%B6