整数的表示
BCD码
BCD(Binary-Coded Decimal)码是一种二进制编码方法,用于表示十进制数字。每个十进制数字(0-9)都使用四位二进制数字表示。
BCD码的基本思路是单独表示每个十进制数字的二进制值,而不是像传统的二进制数系统那样对整个数字进行编码。例如,在传统的二进制编码中,数字"19"会表示为10011,但在BCD中,它被表示为两个独立的数字:“0001”(对应于1)和“1001”(对应于9),因此整个表示为“0001 1001”。
0 - 0000 1 - 0001 2 - 0010
3 - 0011 4 - 0100 5 - 0101
6 - 0110 7 - 0111 8 - 1000
9 - 1001
BCD编码在某些应用中是很有用的,特别是在需要与十进制界面进行交互的地方,如数字显示或某些早期的计算机系统。尽管它不如纯二进制编码效率高,但它简化了与十进制数据的转换过程。
整数的表示
非负数
在程序中使用 unsigned
定义的变量就是非负数表示。
表示方式
在非负数中,数字中的第 $n$ 位的大小就是 $2^{n}$。
对于一个 $n$ 位非负数 $U$,假设第 $i$ 位表示为 $b_{i}$,即该数字的二进制表示为 $U_{\text{binary}} = b_{n-1} \cdots b_{1} b_{0}$,对应的十进制数为:
$$U_{\text{decimal}} = \sum_{i=0}^{n-1} 2^{i} \cdot b_{i}$$
二进制 → 十进制
第7位 | 第6位 | 第5位 | 第4位 | 第3位 | 第2位 | 第1位 | 第0位 |
---|---|---|---|---|---|---|---|
$2^7 = 128$ | $2^6 = 64$ | $2^5 = 32$ | $2^4 = 16$ | $2^3 = 8$ | $2^2 = 4$ | $2^1 = 2$ | $2^0 = 1$ |
10000001
对应的值为 $128 + 1 = 129$10001001
对应的值为 $128 + 8 + 1 = 137$01000001
对应的值为 $64 + 1 = 65$
在现代 64 位计算机中,unsigned
是 32 位非负数。也可以使用 uint16_t
表示 16 位非负数、uint32_t
表示 32 位非负数、uint64_t
表示 64 位非负数。
十进制 → 二进制
将十进制转化为二进制 unsigned 的方法就是使用 $U_{\text{decimal}} = \sum_{i=0}^{n-1} 2^{i} \cdot b_{i}$ 进行反向转换。
如果数字不太复杂,可以利用直觉直接观察,将一个数字转化为若干个 2 的 n 次方之和(1, 2, 4, 8 …)。比如 $73 = 64 + 8 + 1 = 2^6 + 2^3 + 2^0$,如果该数字为 8 位的话,那么该十进制对应的二进制就是 01001001。
第二种方法稍微麻烦点,就是充当人脑计算机,采用除 2 取余法逐步计算余数:
73 ÷ 2 = 36 ... 1
36 ÷ 2 = 18 ... 0
18 ÷ 2 = 9 ... 0
9 ÷ 2 = 4 ... 1
4 ÷ 2 = 2 ... 0
2 ÷ 2 = 1 ... 0
1 ÷ 2 = 0 ... 1
接下来将余数进行逆序重排得到二进制数:1001001,因为是 8 位数,所以在剩下的高位全部填上0,得到结果 01001001。
补码
在程序中使用 short
、int
、long
定义的整形变量就是使用补码(Two’s complement)表示的数字。
表示方式
整形(signed)与非负数(unsigned)的表示方式基本相同,除了最高位。对于一个 $n$ 位数,其最高位为 $-2^{n-1}$ 是一个负数,而不是 $2^{n-1}$。
对于一个 $n$ 位非负数 $S$,假设第 $i$ 位表示为 $b_{i}$,即该数字的二进制表示为 $S_{\text{binary}} = b_{n-1} \cdots b_{1} b_{0}$,对应的十进制数为:
$$S_{\text{decimal}} = \sum_{i=0}^{n-2} 2^{i} \cdot b_{i} - 2^{n-1} \cdot b_{n-1}$$
二进制 → 十进制
以 8 位补码为例说一下如何将 补码表示的二进制 转化为 实际十进制数字。
首先,8 位补码中的每一位的大小为:
第7位 | 第6位 | 第5位 | 第4位 | 第3位 | 第2位 | 第1位 | 第0位 |
---|---|---|---|---|---|---|---|
$-2^7 = -128$ | $2^6 = 64$ | $2^5 = 32$ | $2^4 = 16$ | $2^3 = 8$ | $2^2 = 4$ | $2^1 = 2$ | $2^0 = 1$ |
例子:
10000001
对应的值为 $-128 + 1 = -127$10001001
对应的值为 $-128 + 8 + 1 = -119$01000001
对应的值为 $64 + 1 = 65$
以8位二进制数为例:
- 对于正数,例如 +5,其二进制表示为 00000101,补码也是 00000101。
- 对于负数,例如 -5,首先写出5的二进制表示:00000101。
- 按位取反得到:11111010
- 加1得到:11111011
- 所以,-5的补码是 11111011。
十进制 → 二进制
将十进制转化为二进制补码一种比较简单的方法就是基于 $S_{\text{decimal}} = \sum_{i=0}^{n-2} 2^{i} \cdot b_{i} - 2^{n-1} \cdot b_{n-1}$ 进行直接观察。
比如 $-73 = -128 + 55 = -128 + 32 + 16 + 4 + 2 + 1 = -2^7 + 2^5 + 2^4 + 2^2 + 2^1 + 2^0$,对应的补码为 1011 0111。
另一种就是先计算原码,再将原码转化为补码,具体步骤如下:
- 确定数值的正负性:
- 使用“除2取余法”将 十进制数的绝对值 转换为二进制数。
- 如果十进制数为正数,则其补码与原码相同。
- 如果十进制数为负数,将原码进行 取反加一 得到补码。
比如通过第二种方式将 -73
转为补码:
1. -74 是负数,其绝对值为 73。
2. 除2取余法得到结果为 0100 1001。
3. 取反:1011 0110,加一:1011 0111。
结果为 1011 0111
优点
使用补码可以使得加法、减法和负数的表示变得简单和统一。比如给定两个数字 A 和 B,我们想要计算 C = A + B。A + B 由硬件加法器得到的二进制结果可以直接作为 C 的二进制表示。
补码的设计使得负数的加法变得简单。当使用补码表示时,可以直接将两个数相加,即使其中一个数是负数,而不需要进行任何特殊处理。这简化了计算机硬件的设计。
原码
原码(Sign-Magnitude)是一种简单的方式来表示二进制中的有符号整数。在这种表示法中,数字的最高位(最左边的位)用于表示符号,其余的位表示数字的大小。通常,符号位为0表示正数,而1表示负数。
表示方法
原码的表示方法:
- 正数的原码:最高位为0,其余位表示这个数的绝对值的二进制形式。
- 负数的原码:最高位为1,其余位表示这个数的绝对值的二进制形式。
- 零的原码:符号位可以是0(表示+0)或1(表示-0),但在实际应用中,通常只使用一个零,即符号位为0。
举例(以8位二进制数为例):
+5
的原码是:00000101
-5
的原码是:10000101
缺点
- 存在正零和负零的表示。
- 在进行算术运算时,正数和负数需要不同的处理,这使得硬件设计变得复杂。
由于上述的缺点,原码并不是计算机中最常用的表示法。在现代计算机中,补码是更常用的方式来表示有符号整数,因为它简化了算术运算的处理,并且没有+0和-0的冗余表示。