2018-2019-1 20189206 《深入理解计算机系统》第二章学习笔记
2018-2019-1 20189206 《深入理解计算机系统》第五周学习总结
教材学习内容总结
本章主要研究三种重要的数字表示,分别是无符号编码、补码编码和浮点数编码。其中,无符号编码表示大于或等于零的数字,补码编码用来表示有符号整数,浮点数编码是科学计数法的以2为基数的版本。
第二章 信息的表示和处理
信息存储的方式
- 最小可寻址的内存单位————8位的块,也称为字节
- 机器级程序将内存视为一个非常大的数组,称为虚拟内存
- 内存的每个字节都由唯一的数字来表示,称为地址
十六进制表示方式
-
一个字节的
对应的二进制表示为 \(00000000_2 至 11111111_2\)
对应的十进制表示为 \(0_{10} 至 255_{10}\)
对应的十六进制表示为 \(00_{16} 至 FF_{16}\)(以0x或者0X开头的数字为十六进制表示) -
特殊的转换方法
当值x是2的非负整数的n次幂时即 $$x=2^n$$ 可以将x写成1后面加n个0的形式。
当n表示为 n=i+4j 时(0<=i<=3),x对应的十六进制数字可以写成1、2、4、8后面跟J个0
例如: $$ 512 = 2^9 $$ 知 n=1+2*4 故2048对应的16进制是 0x200
字数据大小
- 每个计算机都有一个字长,用来指明指针数据的标称大小。 字长决定的最重要的系统参数就是虚拟地址空间的最大大小。对于一个字长为w的机器,虚拟地址的范围为$$ 0 至 2^w -1 $$程序最多访问2^w个字节。
- 多字节对象被存储为连续的字节序列,对象的地址为使用字节中最小地址
- 大端法:最高有效字节存储在前面(即先存储最高有效字节)
- 小端法:最低有效字节存储在前面(先存储最低有效字节)
- 假设0x01234567
- 大端法存储为 01 23 45 67 (地址从左至右增大)
- 小端法存储为 67 45 23 01 (地址从左至右增大)
- linux32 Windows和linux64 均为小端机器,可以记为阅读顺序和存储顺序相反。
下面show_bytes
函数的作用是打印出每个以十六进制表示的字节,其中输入是一个字节序列的地址。
void show_bytes(byte_pointer start,size_t len)
{
size_t i;
for(i=0;i<len;i++)
{
printf("%.2x",start[i]);
}
printf("\n");
}
void show_int(int x)
{
show_bytes((byte_pointer)&x,sizeof(int));
}
实现的功能就是输出int型每个字节对应的十六进制表示。
布尔代数
布尔运算 逻辑运算 命题逻辑
~ NOT ┐
& AND ∧
| OR ∨
∧ 异或 ⊕
- 位级运算的常用用法是实现掩码运算,掩码表示从一个数中选出的位的集和。
x & 0xFF 得到的结果就是x最低有效字节组成的值,其余字节被置0,之后表达式~0 得到的结果就是全为1 的掩码。
【注】注意区分C语言中的逻辑运算与位级运算
C语言的逻辑运算是|| &&和!,分别对应命题逻辑中的OR、AND和NOT运算。逻辑运算认为所有的非零值都为真,参数零表示假,运算结果只由0和1。
逻辑运算中如果第一个参数求值就能确定表达式的值,逻辑运算不会对第二个参数求值
位移运算
- 位移运算分为左移和右移
- 左移 X向左移,丢弃最高的k位,并在右端补0
- 右移 X向右移,丢弃最低的k位
- 算数右移 在左端补k个最高有效位的值
- 逻辑右移 在左端补0
几乎所有的编译器/机器组合都会对有符号数使用算数右移,而对于无符号数,右移必须是逻辑右移。
整数表示
无符号数只能表示非负数,而补码编码能够表示负数、0和正数。
-
无符号数的编码
-
无符号数的定义
对于向量 $$ \vec{x} = [x_{w-1},x_{w-2},\cdots,x_0] $$ 有\[B2U_w (\vec{x}) = \sum_{i=0}^{w-1}x_i2^i \] -
无符号数所能表示的值的范围
[0 0 …… 0] 到 [1 1 …… 1]即\[UMax_w = \sum_{i=0}^{w-1}2^i = 2^w-1 \] -
无符号编码的唯一性
函数B2U是一个双射,将每一个长度为w的位向量,映射为0~2^w-1之间的唯一一个值,这种映射是一一对应的关系,即可以反向操作。
-
-
补码编码
最常见的有符号数的计算机表示方式就是补码形式,这种形式下,将字的最高有效位解释为负权。- 补码编码的定义
对于向量 $$ \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-1}x_i2^i \]最高有效位称为符号位,符号位为1时,表示值为负数;符号值为0时,值为非负。
- 补码所能表示的范围
它能表示的最小值是[1 0 …… 0] 其整数值为 $$TMin_w = -2^{w-1} $$
最大值为[0 1 …… 1] 其整数值为$$TMax_w = \sum_{i=0}{w-1}2i = 2^w-1$$- 补码有着与无符号数相同的双射函数
- 最大无符号数刚好比补码的最大值的两倍大一$$UMax_w=2TMax_w + 1 $$
有符号数和无符号数之间的转换
-
强制类型转换的结果保持位不变,只是改变了解释这些位的方式。处理同样字长的有符号数和无符号数之间的转换一般规则是:数值可能会改变,但是位模式不变
-
补码转换为无符号数
对满足$$TMin_w\leq x \leq TMax_w $$的x有:
从以上表达式可以看出,将一个有符号数转为它相应的无符号数时,负数就被转换成了大的整数,非负数则会保持不变。
- 无符号数转换为补码
对满足$$0 \leq u \leq UMax_w $$的x有:
从以上表达式可以看出,讲一个无符号数转换为补码时,U2T把大于2^w-1的数转化成了负数。
从上述两个公式可以看出,对于在范围$$ 0 \leq x \leq TMax_w $$的范围内的数字有着相同的补码和无符号数表示。对于这个范围以外的数需要加上或者减去2^w
- C语言中,当执行一个运算时,它的一个运算数是有符号的,另一个运算数是无符号的,那么C语言会将有符号数强制类型转换为无符号数,并假设这两个数都是非负的。
扩展一个数字位的表示
- 将无符号数转换为一个更大的数据类型——零扩展
- 将补码转换为一个更大的数据类型——符号扩展(添加最高有效位的值)
截断数字
- 截断无符号数
令$$\vec{x} = [x_{w-1},x_{w-2},\cdots,x_0]$$ 现截断该位向量k位的结果是$$x' = x mod 2^k$$
- 截断补码数值
令$$\vec{x} = [x_{w-1},x_{w-2},\cdots,x_0]$$ 现截断该位向量k位的结果是$$x' = U2T_k(x mod 2^k)$$即把最高位的权重从正变为负。
整数运算
- 无符号加法
定义无符号数加法,该操作是把整数和x+y截断为w位得到的结果,再把这个结果看作是一个无符号数。可以被看作是一种形式的模运算
对于x、y满足$$ 0 \leq x,y<2^w $$有:
当x+y的结果s小于x或者小于y的时候,可以判定无符号数的加法出现了溢出。
- 无符号数求反
模数加法形成了一个阿贝尔群,对于每个x值必定有有一个加法逆元。
对于满足$$ 0 \leq x <2^w $$的任意x值,其无符号加法逆元为
- 补码加法
对于满足$$-2^{w-1} \leq x,y \leq 2^{w-1}-1 $$的整数x和y 有
- 补码的非
对于满足$$ TMin_w \leq x <TMin_w $$的任意x值,其补码的非为
-
无符号和补码乘法
将一个数截断w就等价于计算该值的模2^w
对于无符号和补码乘法来说,乘法运算的位级表示都是一样的。 -
乘以常数
由于在大多数机器上,整数乘法的指令需要10个或者更多的时钟周期,其他整数运算只需要1个时钟周期。编译器使用了优化,试着用位移和加法的运算组合代替乘以常数因子的乘法。
原理: 设x的位模式为 \([x_{w-1},x_{w-2},\cdots,x_0]\)表示无符号整数,那么对于任何$k \leq 0 $都认为 \([x_{w-1},x_{w-2},\cdots,x_0,0,0,\cdots,0]\) 给出了 \(x*2^k\) 的w+k位的无符号表示,右边增加了k个0。
可以看出,左移一个数值相当于执行一个与2的幂相乘的无符号乘法
例如:一个程序包含表达式 x*14 利用 $14 =23+22+2^1 $ 可以将该乘法重写(x<<3)+(x<<2)+(x<<1),将一个乘法替换为位移和两个加法。
或者利用 $14 =24-21 $ 重写成 (x<<4)-(x<<1)
- 除以2的幂
- 除以2的幂的无符号除法
假设有无符号数值x和k,且 $0 \leq k <w $ 则表达式 x>>k 产生结果\(\lfloor x/2^k \rfloor\) - 除以2的幂的补码除法
假设有补码数值x和无符号数值k,且 $0 \leq k <w $ 则表达式执行算术位移 x>>k 产生结果\(\lfloor x/2^k \rfloor\)
- 除以2的幂的无符号除法
浮点数
- 二进制小数
表示方法:$ b_m b_{m-1} \cdots b_1 b_0.b_{-1} b_{-2} \cdots b_{-n} $
这个表达描述的定义如下:
- IEEE浮点表示
-
浮点标准用$$ V= (-1)^s * M * 2^E $$ 来表示一个数
- 符号:s决定了这个数是负数(s=1)还是正数(s=0)
- 尾数:M是一个二进制小数,范围是1~2-ε 或者 0~1-ε
- 阶码:E的作用是对浮点数加权,这个权重是2的E次幂
-
将浮点数的位表示划分为3个字段,分别对这些值编码
- 一个单独的符号位s
- k位的阶码字段\(exp=e_{k-1} \cdots e_1 e_0\)编码阶码E
- n位的小数字段$frac=f_{n-1} \cdots f_1 f_0 $编码尾数M
-
规格化的值
- exp的位模式不会全为0或全为1 阶码字段被表示为以偏置形式表示的有符号整数。E= e-bias e为无符号数 \(bias=2^{k-1}-bais\)
- 小数字段frac,尾数定义为M=1+f
-
非规格化的值
- 阶码E全为0时是非规格化的形式,此时 E=1-Bias 尾数值M=f
-
特殊值
- 阶码全为1时,得到的值表示无穷
-
【注】浮点运算只由有限的范围和精度,并且不遵守结合律。
总结
本章重点在于用数学公式准确定义出计算机中使用的几种数据类型,内容很多,需要好好总结和复习。通过直接操作数字级的位表示,得到了几种算数运算的方式。针对不同的机器,变量类型存储的大小也不同、存储方式不同,为了能使编写的程序在全部范围内正确工作,可以实现跨越不同的机器、操作系统和编译器的组合,对于这种数学原理的学习是十分重要的。
第二章主要从信息存储的方式、整数表的方法(无符号编码、补码编码)以及相关操作、整数运算(无符号运算、补码运算)、浮点数(两种表示方式)等方面介绍了数据的存储表示方式,进行计算的结果等内容。