目录
- 进制的定义
- 常用的进制与换算
- 十进制到二进制的转换
- 二进制到十六进制、十六进制到二进制的转换
- 二进制向 n 进制的转换
- 有符号数处理(Signed Representation)
- 无符号整数(Unsigned Integers)
- 有符号整数(Signed Intergers)
- 原码(Sign-Magnitude)
- 移码(Biased Notation)
- 反码(One's Complement)
- 补码(Two's Complement)
- 求补码的十进制数
- 溢出 (Overflow)
- 符号拓展(Sign Extension)
本文章系计算机体系结构课程 UCB CS61C: Great Ideas in Computer Architecture 的学习笔记。
进制的定义
我们可以将一个十进制的数字分解为以 10 为基底的整数次幂构成的多项式,每一项的系数取值在 0 到 9 之间。
由此我们可以推断出
B
B
B 进制下任意
n
n
n 位数的通式( n-digit number in base B ):
d
n
−
1
d
n
−
2
.
.
.
d
1
d
0
=
d
n
−
1
×
B
n
−
1
+
d
n
−
2
×
B
n
−
2
+
.
.
.
+
d
1
×
B
1
+
d
0
×
B
0
d_{n-1}d_{n-2}...d_1d_0= d_{n-1} \times B^{n-1} + d_{n-2}\times B^{n-2} + ...+ d_1\times B^1+ d_0 \times B^0
dn−1dn−2...d1d0=dn−1×Bn−1+dn−2×Bn−2+...+d1×B1+d0×B0
或者直接写作
N
=
∑
i
=
0
n
−
1
x
i
B
i
.
N=\sum_{i=0}^{n-1} x_i B^i.
N=i=0∑n−1xiBi.
常用的进制与换算
进制 | 符号 | 标记法 |
---|---|---|
十进制(Decimal) | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 | 9472ten = 9472 |
二进制(Binary) | 0, 1 | 101011two = 0b101011 |
十六进制(Hexadecimal) | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F | 2A5Dhex = 0x2A5D |
十进制 | 二进制 | 十六进制 |
---|---|---|
0 | 0000 | 0 |
1 | 0001 | 1 |
2 | 0010 | 2 |
3 | 0011 | 3 |
4 | 0100 | 4 |
5 | 0101 | 5 |
6 | 0110 | 6 |
7 | 0111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
10 | 1010 | A |
11 | 1011 | B |
12 | 1100 | C |
13 | 1101 | D |
14 | 1110 | E |
15 | 1111 | F |
- 只有二进制允许使用 0 和 1 来表示数字,即以位(bits,或称字节)表示。
- 十六进制的每位数字被称为“半字节”(nibble)。
- 通过数字的前缀表示所使用的进制(基数):十进制不做处理,二进制为
0b
,十六进制为0x
。 - 210 - Kibi(千字节),220 - Mebi(兆字节),230 - Gibi(吉字节),240 - Tebi(太字节),250 - Pebi(拍字节),260 - Exbi(艾字节),270 - Zebi(泽字节),280 - Yobi(尧字节).
我们在进行进制转换的时候,最好按照如下顺序:
十进制到二进制的转换
我们反复对 2 取余直到商为 0,将余数逆序依次排列即可。例如,
1
4
t
e
n
14_{ten}
14ten:
14
≡
0
(
m
o
d
2
)
7
≡
1
(
m
o
d
2
)
3
≡
1
(
m
o
d
2
)
1
≡
1
(
m
o
d
2
)
\begin{aligned} 14 &\equiv 0\ (\bmod\ 2) \\ 7 &\equiv 1\ (\bmod\ 2) \\ 3 &\equiv 1\ (\bmod\ 2) \\ 1 &\equiv 1\ (\bmod\ 2) \\ \end{aligned}
14731≡0 (mod 2)≡1 (mod 2)≡1 (mod 2)≡1 (mod 2)
那么,
1
4
t
e
n
14_{ten}
14ten 即 0b 1110
,我们也可以使用短除法进行简便计算。
二进制到十六进制、十六进制到二进制的转换
十六进制的每一位可以表示 0~15 之间的数值,而二进制的四个位恰好可以表达 0~15 之间的数值,故我们依据上述的进制换算表,将二进制数中的位 4 个为一组地分别翻译为相应的十六进制数中的半字节,组成十六进制数。例如二进制数 0b 1001 1000 1111 0011
,0b 1001
对应 0x 9
,0b 1000
对应 0x 8
,0b 1111
对应着 0x F
,0b 0011
对应着 0x 3
,因此对应的十六进制数为 0x 98F3
。
我们可以知道,二进制的 4 个位对应着十六进制的 1 个半字节,因此我们将十六进制数每一个半字节都转换为二进制的 4 个位,即可组成对应的二进制数。
二进制向 n 进制的转换
同理,对于从二进制到四进制的转换,四进制的每一位可以表示 0~3 之间的数值,而二进制的两个位可以表示之,因此我们直接将二进制数两两分组,分别翻译,最终转换为四进制数;八进制亦然,八进制的每一位可以表示 0~7 之间的数值,而二进制的三个位也可以表示之,将二进制数 3 个为一组地分别翻译,得出八进制数。
对于不足相应组内元素数(例如,3 个为一组,剩下 1 个位)的一组,我们可以为其扩充,在有效值前面增添虚构、不改变数值大小的 0,举个例子,0b 1 111 001 001
中的 0b 1
就可以同义转换为 0b 001
,这样就满足了八进制转换的 3 个为一组的分配原则。
有符号数处理(Signed Representation)
无符号整数(Unsigned Integers)
- 无符号(unsigned)即非负(non-negative),无法表达负数。
- 对于 n n n 位数最大正整数, 1... 1 t w o = ( 2 n − 1 ) t e n . 1...1_{two} = (2^n-1)_{ten}. 1...1two=(2n−1)ten.
有符号整数(Signed Intergers)
原码(Sign-Magnitude)
为了可以同时表达正负数,我们取最高有效位来确定符号,0 表示正数,1 表示负数,例如 00 1 t w o = 1 t e n 001_{two} = 1_{ten} 001two=1ten, 10 1 t w o = − 1 t e n . 101_{two} = -1_{ten}. 101two=−1ten.
实际上,原码会存在两个 0: 0... 0 t w o = 1... 0 t w o = ± 0 t e n . 0...0_{two} = 1...0_{two} = \pm0_{ten}. 0...0two=1...0two=±0ten.
相应地,对于 n n n 位数最大正(负)整数, 01... 1 t w o = ( 2 n − 1 − 1 ) t e n 01...1_{two} = (2^{n-1}-1)_{ten} 01...1two=(2n−1−1)ten, 11... 1 t w o = − ( 2 n − 1 − 1 ) t e n . 11...1_{two} = -(2^{n-1}-1)_{ten}. 11...1two=−(2n−1−1)ten.
移码(Biased Notation)
然而,当我们原先设定的位数不满足较大数的表达1,抑或是单纯地将两个负数相加表达出较小的负数而无法表达出较大的负数2的时候,我们可以尝试套用无符号的情况,将 0 偏移到无符号整数取值范围的中间,形成偏移,即所谓移码(Biased Notation),其值应等于此时的“无符号整数”减去偏移量(即偏置):
00
0
t
w
o
=
−
3
t
e
n
00
1
t
w
o
=
−
2
t
e
n
01
0
t
w
o
=
−
1
t
e
n
01
1
t
w
o
=
0
t
e
n
10
0
t
w
o
=
+
1
t
e
n
10
1
t
w
o
=
+
2
t
e
n
11
0
t
w
o
=
+
3
t
e
n
11
1
t
w
o
=
+
4
t
e
n
\begin{aligned} 000_{two} &= -3_{ten} \\ 001_{two} &= -2_{ten} \\ 010_{two} &= -1_{ten} \\ 011_{two} &= 0_{ten} \\ 100_{two} &= +1_{ten} \\ 101_{two} &= +2_{ten} \\ 110_{two} &= +3_{ten} \\ 111_{two} &= +4_{ten} \\ \end{aligned}
000two001two010two011two100two101two110two111two=−3ten=−2ten=−1ten=0ten=+1ten=+2ten=+3ten=+4ten
对于 n n n 位数,在这种表示方法中使用的、约定俗成的固定偏移量习惯偏置(Conventional Bias)应为 2 n − 1 − 1 2^{n-1}-1 2n−1−1。基于这种前提, 01... 1 t w o = 0 t e n 01...1_{two}=0_{ten} 01...1two=0ten, 最大能表示的负(正)整数分别为 0... 0 t w o = − ( 2 n − 1 − 1 ) t e n 0...0_{two} = -(2^{n-1} -1)_{ten} 0...0two=−(2n−1−1)ten, 1... 1 t w o = ( 2 n − 1 ) t e n . 1...1_{two} = (2^{n-1} )_{ten}. 1...1two=(2n−1)ten.
移码克服了原码出现的问题,但是采用移码表示的 0 不再是单纯的 0 了。
反码(One’s Complement)
正数的反码与其原码相同,负数的反码是相应绝对值的原码翻转(0 替换为 1,1 替换为 0,也称补充位)后的结果,即对其原码(符号位除外)按位取反。如图,实际上它们是中心对称的:
00
0
t
w
o
=
+
0
t
e
n
00
1
t
w
o
=
+
1
t
e
n
01
0
t
w
o
=
+
2
t
e
n
01
1
t
w
o
=
+
3
t
e
n
10
0
t
w
o
=
−
3
t
e
n
10
1
t
w
o
=
−
2
t
e
n
11
0
t
w
o
=
−
1
t
e
n
11
1
t
w
o
=
−
0
t
e
n
\begin{aligned} 000_{two} &= +0_{ten} \\ 001_{two} &= +1_{ten} \\ 010_{two} &= +2_{ten} \\ 011_{two} &= +3_{ten} \\\\ 100_{two} &= -3_{ten} \\ 101_{two} &= -2_{ten} \\ 110_{two} &= -1_{ten} \\ 111_{two} &= -0_{ten} \\ \end{aligned}
000two001two010two011two100two101two110two111two=+0ten=+1ten=+2ten=+3ten=−3ten=−2ten=−1ten=−0ten
反码仍然有两个 0。最大负整数为 10... 0 t w o = − ( 2 n − 1 − 1 ) t e n 10...0_{two} = -(2^{n-1}-1)_{ten} 10...0two=−(2n−1−1)ten;最大正整数为 01... 1 t w o = ( 2 n − 1 − 1 ) t e n . 01...1_{two} = (2^{n-1}-1)_{ten}. 01...1two=(2n−1−1)ten.
然而,反码仍未解决加减法运算的复杂性,容易溢出。
补码(Two’s Complement)
借用反码的核心思想,补码对一个数按位取反,随后增加一个“转折”(twist),具体的量为 13。补码的重要特性是,正数的补码就是其本身,而负数的补码则是其绝对值的补码:
00
0
t
w
o
=
+
0
t
e
n
00
1
t
w
o
=
+
1
t
e
n
01
0
t
w
o
=
+
2
t
e
n
01
1
t
w
o
=
+
3
t
e
n
10
0
t
w
o
=
−
4
t
e
n
10
1
t
w
o
=
−
3
t
e
n
11
0
t
w
o
=
−
2
t
e
n
11
1
t
w
o
=
−
1
t
e
n
\begin{aligned} 000_{two} &= +0_{ten} \\ 001_{two} &= +1_{ten} \\ 010_{two} &= +2_{ten} \\ 011_{two} &= +3_{ten} \\\\ 100_{two} &= -4_{ten} \\ 101_{two} &= -3_{ten} \\ 110_{two} &= -2_{ten} \\ 111_{two} &= -1_{ten} \\ \end{aligned}
000two001two010two011two100two101two110two111two=+0ten=+1ten=+2ten=+3ten=−4ten=−3ten=−2ten=−1ten
那么,对于 n n n 位数,最大负整数为 10... 0 t w o = ( − 2 n − 1 ) t e n 10...0_{two} = (-2^{n-1})_{ten} 10...0two=(−2n−1)ten,最大正整数为 01... 1 t w o = ( 2 n − 1 − 1 ) t e n . 01...1_{two} = (2^{n-1}-1)_{ten}. 01...1two=(2n−1−1)ten.
基于这种表示法,我们统一了加减法运算——在补码表示法中,减法可以转换为加法,减去一个数等价于加上这个数的补码。这样,计算机只需要实现加法器,而不需要单独的减法器,从而简化了硬件设计;
简化了溢出处理—— 使用补码时,溢出处理变得简单。在进行加法运算时,若结果超出了表示范围,与无符号数的处理方式一致,最高位的进位会被自然丢弃。这种特性使得补码在处理溢出时不需要额外的逻辑;
使用了唯一零值表示——补码避免了其他表示法中可能出现的正零和负零的问题,简化了比较和判断逻辑;
实现了符号位自然扩展——在进行符号扩展时,补码的符号位可以直接复制到更高位,而不需要额外的转换。例如,一个 8 位补码数 11111000
(表示 -8)扩展到 16 位时,可以直接变成 11111111 11111000
,这保持了数值不变,简化了扩展操作。
因此,补码是计算机中最为常用的数值表示法。
求补码的十进制数
对于一个给出的二进制补码,其最高有效位为该数的符号位。例如,补码 0b 1110
,则该数必为负数,那么我们可以得出其十进制数为
−
2
3
+
2
2
+
2
1
=
−
2.
-2^3+2^2+2^1=-2.
−23+22+21=−2.
而依据二进制补码的原理,我们可以用另外的求法——对其求补码而转化为原码,也就是翻转位并加 1,根据原码求出对应的十进制,而原来二进制的补码由于逆运算的操作符号相反。仍以 0b 1110
举例,先翻转位 0b 0001
,再加 1,即 0b 0010
,由于逆运算,注意符号,最终应为 -2。
溢出 (Overflow)
当算术运算的结果无法被有限数量的硬件位(hardware bits)表示的时候,溢出就会发生。
解决方案是增加更多的硬件位,直到再次用尽。
符号拓展(Sign Extension)
要使用更多的位表达更大的数,就需要使用更多的位来表示先前的数:
- 对于正数,我们只需要增加更多的
leading zeros
即可。 - 对于负数,采用原码时,在符号位添加相应的 0;采用反码/补码时,复制一份 MSB 填充到新位。例如,
0b 11 = 0b 1001, 0b 11 = 0b 1111
。
由于原码采用最高有效位表示符号的方式,当我们逐步增加值的大小直到超出原来位数可表达的范围而需要扩充时,补充标志性的正负符号就会比较麻烦。 ↩︎
正负仅仅是“符号”,不表明大小,“最大负整数”是相对于绝对值意义的。 ↩︎
按位取反后的结果再加 1。 ↩︎