前言
在本章开始有一段阅读本章的建议,在学习底层理论时,都应该保持这样的阅读习惯。
为了帮助你阅读,这部分内容安排如下:首先给出以数学形式表示的属性,作为原理。然后,用例子和非形式化的讨论来解释这个原理。我们建议你反复阅读原理描述和它的示例与讨论,直到你对该属性的说明内容及其重要性有了牢固的直觉。对于更加复杂的属性,还会提供推导,其结构看上去将会像一个数学证明。虽然最终你应该尝试理解这些推导,但在第一次阅读时你可以跳过它们。
我们也鼓励你在阅读正文的过程中完成练习题,这会促使你主动学习,帮助你理论联系实际。有了这些例题和练习题作为背景知识,再返回推导,你将发现理解起来会容易许多。
“牢固的直觉”,应该对属性如数家珍,像常识1+1=2能脱口而出。
字节顺序
数据最高有效数据在低位,还是最低有效位在低位,也就是数据在内存中的顺序。
大端法:有效高位在低地址端;
小端法:有效低位在低地址端;
例子:
假设变量x的类型为int,位于地址0x100处,它的十六进制值为0x01234567。地址范围0x100 - 0x103的字节顺序依赖于机器的类型:
布尔代数
布尔代数的相关定理
分配律1:a | (b & c) = (a | b) & (a | c)
分配律2:a & (b | c) = (a & b) | (a & c)
异或:(a ^ b)^a = b
a ^ b = (a & ~b) | (~a & b)
这个属性在 中要用到,
他的证明可能用维恩图来表示就很明白了
位移运算
c语言
x << k: 逻辑左移,左端丢掉k位,右端补k个0;
x >> k: 逻辑右移,右端丢掉k位,左端补k个0;
x >> k: 算术右移,右端丢掉k位,左端补k个最高有效位,(补码的负数,就是补个1)
C/C++语言中逻辑右移和算数右移共享同一个运算符>>。编译器决定使用逻辑右移还是算数右移,根据的是运算数的类型。如果运算数类型是unsigned
则采用逻辑右移,而signed
则采用算数右移。对于signed类型的数据,如果需要使用逻辑右移,需要进行类型转换。
为什么补码的算术右移k位,左边得补k位最高有效位呢?p55页补码的数符号扩展
证明了。
java 有明确的右移定义
x << k : 逻辑左移
x >>> k: 逻辑右移
x >> k : 算术右移
位移优先级
在C、JAVA中 + - 优先级高于位移 ,注意使用();
整数表示
整数的数据与算术操作术语。下标w表示数据中的位数
符号 类型 含义 B 2 T w 函 数 二 进 制 转 补 码 B 2 U w 函 数 二 进 制 转 无 符 号 数 U 2 B w 函 数 无 符 号 数 转 二 进 制 U 2 T w 函 数 无 符 号 转 补 码 T 2 B w 函 数 补 码 转 二 进 制 T 2 U w 函 数 补 码 转 无 符 号 T M a x w 函 数 最 大 补 码 值 T M i n w 函 数 最 小 补 码 值 U M a x w 函 数 最 大 无 符 号 数 + w t 操 作 补 码 加 法 + w u 操 作 无 符 号 加 法 ∗ w t 操 作 补 码 乘 法 ∗ w u 操 作 无 符 号 乘 法 − w t 操 作 补 码 减 法 − w u 操 作 无 符 号 减 法 \begin{array}{|c|c|c|} \hline \text{符号} & \text{类型} & \text{含义}\\ \hline B2T_w & 函数 & 二进制转补码\\ \\ B2U_w & 函数 & 二进制转无符号数\\ \\ U2B_w & 函数 & 无符号数转二进制\\ \\ U2T_w & 函数 & 无符号转补码\\ \\ T2B_w & 函数 & 补码转二进制\\ \\ T2U_w & 函数 & 补码转无符号\\ \\ TMax_w & 函数 & 最大补码值\\ \\ TMin_w & 函数 & 最小补码值\\ \\ UMax_w & 函数 & 最大无符号数\\ \\ +{^t_w} & 操作 & 补码加法\\ \\ +{^u_w} & 操作 & 无符号加法\\ \\ *{^t_w} & 操作 & 补码乘法\\ \\ *{^u_w} & 操作 & 无符号乘法\\ \\ -{^t_w} & 操作 & 补码减法\\ \\ -{^u_w} & 操作 & 无符号减法\\ \hline \end{array} 符号B2TwB2UwU2BwU2TwT2BwT2UwTMaxwTMinwUMaxw+wt+wu∗wt∗wu−wt−wu类型函数函数函数函数函数函数函数函数函数操作操作操作操作操作操作含义二进制转补码二进制转无符号数无符号数转二进制无符号转补码补码转二进制补码转无符号最大补码值最小补码值最大无符号数补码加法无符号加法补码乘法无符号乘法补码减法无符号减法
无符号数编码
- 2.1 无符号编码定义 P44
对于向量 x ⃗ = [ x w − 1 , x w − 2 , ⋯ , x 0 ] : B 2 U w ( x ⃗ ) ≐ ∑ i = 0 w − 1 x i 2 i \begin{aligned} \text{对于向量} \vec{x}=[x_{w-1}, x_{w-2}, \cdots , x_0]: &\\ & B2U_w(\vec{x}) ≐ \sum_{i=0} ^{w-1} {x_i2^i} \end{aligned} 对于向量x=[xw−1,xw−2,⋯,x0]:B2Uw(x)≐i=0∑w−1xi2i
“≐” 左边被定义为等于右边 ,即表示为,定义为的意思
补码的定义
- 2.3 补码编码定义 P44
对于向量 x ⃗ = [ x w − 1 , x w − 2 , ⋯ , x 0 ] : B 2 T w ( x ⃗ ) ≐ x w − 1 2 w − 1 + ∑ i = 0 w − 2 x i 2 i \begin{aligned} \text{对于向量} \vec{x}=[x_{w-1}, x_{w-2}, \cdots , x_0]: &\\ & B2T_w(\vec{x}) ≐ x_{w-1}2^{w-1} + \sum_{i=0} ^{w-2} {x_i2^i} \end{aligned} 对于向量x=[xw−1,xw−2,⋯,x0]:B2Tw(x)≐xw−12w−1+i=0∑w−2xi2i
原码,反码,补码的发展历史
- 原码 sign-magnitude
英文大意是“符号数值”。最高位为符号位,0代表正常,1代表负数,其它化表示数值,符合人的思维。但机器是很难理解正负的,设计出一个能让机器判断正负的电路也是十分复杂和困难的。
原码编码定义 P47
对于向量
x
⃗
=
[
x
w
−
1
,
x
w
−
2
,
⋯
,
x
0
]
:
B
2
S
w
(
x
⃗
)
≐
−
x
w
−
1
⋅
∑
i
=
0
w
−
2
x
i
2
i
\begin{aligned} \text{对于向量} \vec{x}=[x_{w-1}, x_{w-2}, \cdots , x_0]: &\\ & B2S_w(\vec{x}) ≐ -x_{w-1} \cdot \sum_{i=0} ^{w-2} {x_i2^i} \end{aligned}
对于向量x=[xw−1,xw−2,⋯,x0]:B2Sw(x)≐−xw−1⋅i=0∑w−2xi2i
- 反码 ones’complement
英文直译是“很多个1的补充”。正数与原码一样,我们都知道w位二进制数表示的最大范围是 2 w − 1 2^w-1 2w−1,用位向量来表示就是 [ 1 w − 1 , 1 w − 2 . . . . 1 1 , 1 0 ] [1_{w-1}, 1_{w-2}....1_1,1_0] [1w−1,1w−2....11,10],就是很多个1。假设x是正数,求-x的补码的表示就是求[11111…111]-x,也就是 ( 2 w − 1 − x ) (2^w-1-x) (2w−1−x)(其中w为位数)。
另一种计算方式就是用原码计算,原码的符号位不变,剩下的数值位全部取反,比如1011的反码就位1100。
还有一种证明方式是:每一位用1去减,求其和:
x ‾ = ∑ i = 0 w − 1 ( 1 − x i ) 2 i = ∑ i = 0 w − 1 2 i − ∑ i = 0 w − 1 x i 2 i = 2 w − 1 − 1 − x \overline{x}=\sum_{i=0}^{w-1} {(1 - x_i)2^i} = \sum_{i=0}^{w-1} {2^i} - \sum_{i=0}^{w-1} {x_i2^i} = 2^{w-1} - 1 - x x=i=0∑w−1(1−xi)2i=i=0∑w−12i−i=0∑w−1xi2i=2w−1−1−x
这样反码的表示的真正值就是:
反码编码定义 P47
对于向量
x
⃗
=
[
x
w
−
1
,
x
w
−
2
,
⋯
,
x
0
]
:
B
2
O
w
(
x
⃗
)
≐
−
x
w
−
1
(
2
w
−
1
−
1
)
+
∑
i
=
0
w
−
2
x
i
2
i
\begin{aligned} \text{对于向量} \vec{x}=[x_{w-1}, x_{w-2}, \cdots , x_0]: &\\ & B2O_w(\vec{x}) ≐ -x_{w-1}(2^{w-1} - 1) + \sum_{i=0} ^{w-2} {x_i2^i} \end{aligned}
对于向量x=[xw−1,xw−2,⋯,x0]:B2Ow(x)≐−xw−1(2w−1−1)+i=0∑w−2xi2i
反码实际上可以用来做二进制运算,计算机只有加法器,反码如何做减法呢?讲这个之前得先了解模。
模
首先要介绍一下模的概念:“模”是指一个计量系统的计数范围。如时钟等。计算机也可以看成一个计量机器,它也有一个计量范围,即都存在一个“模”。
例如:
时钟的计量范围是1~12,模=12。表示w位的计算机计量范围是
0
−
2
w
−
1
0-2^{w -1}
0−2w−1,模=
2
w
2^w
2w 。
“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算。在计算机中,计算加法要比计算减法要容易和便宜,那么到底如何在计量装置中用加法代替减法呢?这里我们可以用时钟举一个例子:
时钟的时针,目前它指向9。如果我们想让他回退到4。我们可往后退5格(9-5),也可向前进7格(9+7 = 12 + 4)
反码的模,就是 2 w − 1 2^w-1 2w−1。反码计算若最高位相加后产生进位,则最后得到的结果要加1。
原码和反码的缺陷,对于原码来说[1000]也表示0,对于反码来说[1111]也表示0。一般我们把[0000]称为+0,而另一种表示方法称为-0。一个数字有两种编码方式,这显然是不合理的!就像时钟用模12, 12点就有两个表示,0,12;
- 补码 Two’s complement
补码用的模是2w,这也是英文名为Two’s complement的原因,意为2的补,这里只有一个2,所以用Two’s complement。
补码似乎已经十分完美了,它不仅解决了0的编码重复问题(在补码中0只有[0000]一种表示),也成功的把减法变成了加。
2.3 补码编码定义 P44
对于向量
x
⃗
=
[
x
w
−
1
,
x
w
−
2
,
⋯
,
x
0
]
:
B
2
T
w
(
x
⃗
)
≐
x
w
−
1
2
w
−
1
+
∑
i
=
0
w
−
2
x
i
2
i
\begin{aligned} \text{对于向量} \vec{x}=[x_{w-1}, x_{w-2}, \cdots , x_0]: &\\ & B2T_w(\vec{x}) ≐ x_{w-1}2^{w-1} + \sum_{i=0} ^{w-2} {x_i2^i} \end{aligned}
对于向量x=[xw−1,xw−2,⋯,x0]:B2Tw(x)≐xw−12w−1+i=0∑w−2xi2i