《深入理解计算机系统》CSAPP(二)

《深入理解计算机系统》CSAPP(二)

信息的表示和处理
  • 无符号编码
    • 表示≥0的数字
  • 补码编码
    • 表示有符号整数
  • 浮点数编码
    • 表示实数的科学计数法的以2为基数的版本
  • 溢出
    • 计算机以有限位表示结果
    • 结果过大不能表示导致溢出
    • 如$200 \times 300 \times 400 \times 500 = -884901888$
    • 整数的计算机运算满足真正整数运算的许多性质
      • 如乘法满足结合律和交换律
  • 浮点运算
    • 溢出产生特殊值+∞
    • 一组正数的乘积总是正的
    • 不可结合
      • $3.14 + (10^{20} - 10^{20}) = 3.14$
      • $(3.14 + 10^{20}) - 10^{20} = 0$
  • 整数编码虽然只能编码一个较小的范围,但表示精确
  • 浮点数编码虽然可以编码一个较大的范围,但表示近似

信息存储

十六进制表示法

  • 用十六进制(hex)来表示值
  • ‘0’ $\sim$ ‘9’,‘A’ $\sim$ ‘F’,表示16个值
  • C语言中以0x或0X开头
  • 熟悉十六进制,十进制和二进制之间的相互转化


字数据大小

  • 每台计算机都有一个字长,指明指针数据的标称大小
  • 虚拟地址以这样的一个字来编码$\rightarrow$字长决定的最重要的系统参数就是虚拟地址空间的最大大小
  • 对一个字长为w位的机器而言,虚拟地址的范围为$0 \sim 2^{w}-1$,程序最多访问$2^{w}$个字节
  • 大多数64位机器可以运行32位机器编译的程序,这是一种向后兼容

    • 当程序prog.c用如下伪指令编译后

      linux> gcc -m32 prog.c

      该程序可以在32位或64位机器上正确运行

    • 用下述伪指令编译后

      linux> gcc -m64 prog.c

      就只能在64位机器上运行

    • 将程序称为“32位程序”或“64位程序”时,区别在于该程序是如何编译的,而不是其运行的机器类型
    • 计算机和编译器支持多种不同方式编码的数字格式
    • C语言标准对不同数据类型的数字范围设置了下界,但是却没有上界


寻址和字节顺序

  • 大多数intel机都只用小端模式
  • 双端法
    • 既可以配置成大端也可以配置成小端
    • 但一旦选定了操作系统,其字节顺序就固定了
  • 书写字节序列时最低位字节在左边,最高位字节在右边
  • C语言中可通过强制类型转换联合来允许一种数据类型引用一个对象,而这种数据类型与创建这个对象时定义的数据类型不同
  • 用浮点型和整型表示同一个数据时,将十六进制转化为二进制后,并进行适当的移位,就会发现一个有13个相匹配的位的序列。
  • 如下图,分别用整型和浮点型表示12345
  • 而这并不是巧合,我们后面会进行相关学习


表示字符串

  • C语言中字符串被编码为一个以NULL字符结尾的字符数组
  • 文本数据比二进制数据具有更强的平台独立性


表示代码

1
2
3
int sum(int x, int y) {
return x + y;
}
  • 编译上述代码时,在不同机器上生成的机器代码如下
  • 发现指令编码不同
  • 不同机器类型使用不同且不兼容的指令和编码方式
  • 二进制代码不兼容


布尔代数简介



C语言中的位级运算

  • C语言支持按位布尔运算
  • 位运算实现掩码运算


C语言中的逻辑运算

  • 逻辑运算符
    • ||、&&、!
    • 如果对第一个参数求值就能确定表达式的值,那么逻辑运算符就不会对第二个参数求值
      • a && 5 / a 不会造成被零除
      • p && *p++也不会导致间接引用空指针
  • 不要混淆逻辑运算和位运算


C语言中的移位运算

  • 左移
    • <<
    • x << k
    • 右端补k个0
  • 右移
    • >>
    • x >> k
    • 机器支持逻辑右移算术右移
    • 逻辑右移
      • 左端补k个0
    • 算术右移
      • 左端补k个最高有效位的值
  • C语言中几乎所有编译器都对有符号数使用算术右移,对无符号数进行逻辑右移
  • 注意移位运算的优先级!




整数表示

  • 相关的数学术语


整数数据类型


  • 取值范围不对称
    • 负数范围比正数范围大1
  • C语言标准定义了每种数据类型必须能够表示的最小的取值范围


无符号数的编码

  • 无符号数编码的定义
    • 对向量$\vec{x}=[x_{w-1},x_{w-2},…,x_{0}]:$
    • 如$B2U_4([0101])=0\cdot2^3+1\cdot2^2+0\cdot2^1+1\cdot2^0=0+4+0+1=5$
  • $0~2^w-1$
  • 编码具有唯一性


补码编码

  • 最高有效位解释为负权
  • 补码编码的定义
    • 向量$\vec{x}=[x_{w-1},x_{w-2},…,x_{0}]:$
    • 最高有效位$x_{w-1}$也称为有效位
      • 他为1时,表示值为负
      • 他为0时,表示值为正
      • 如$B2U_4([0101])=-0\cdot2^3+1\cdot2^2+0\cdot2^1+1\cdot2^0=0+4+0+1=5$
      • $B2U_4([1011])=-1\cdot2^3+0\cdot2^2+1\cdot2^1+1\cdot2^0=-8+0+2+1=-5$
  • 表示最小值的向量为[10…0],值为$-2^{w-1}$
  • 最大值的向量为[011…1],值为$2^{w-1}-1$
  • $-2^{w-1}~2^{w-1}-1$
  • 补码具有唯一性
  • 注意事项
    • 补码范围不对称:|TMin|=|TMax|+1
    • 最大无符号数值刚好比补码的最大值的两倍大1:$UMax_w=2TMax_w+1$
  • 几乎所有现代机器都使用补码


有符号数的其他表示方法

  • 反码
    • 除了最高有效位的权为$-(2^{w-1}-1)$而不是$-2^{w-1}$,他和补码是一样的
  • 原码
    • 最高有效位是符号位,用来确定剩下的位应该取负权还是正权
  • 这两种方法都有一个奇怪的属性
    • 把[00…0]都解释为+0
    • -0在反码中表示为[11…1],在原码中表示为[10…0]


有符号数和无符号数之间的转换

  • C语言允许各种不同的数字数据类型之间做强制类型转换
  • 强制类型转换的结果保持位置不变,只是改变了解释这些位的方式


C语言中的有符号数与无符号数

  • 要创建一个无符号常量,必须加上后缀字符’U’或者’u’
    • 如12345U或者0x5A11u
  • 如果无符号和有符号的数同时参与运算,C语言会将无符号强制转换为有符号
    • 这会导致非直观的影响


扩展一个数字的位表示

  • 无符号数的零扩展
    • 定义宽度为w的位向量$\vec{u}=[u_{w-1},u_{w-2},…,u_0]$和宽度位$w^{‘}$的位向量$\vec{u^{‘}}=[0,…,0,u_{w-1},u_{w-2},…,u_0]$,其中$w_{‘}>w$。
    • 则$B2U_w(\vec{u})=B2U_{w^{‘}}(\vec{u^{‘}})$
  • 补码数的符号扩展
    • 定义宽度为w的位向量$\vec{x}=[x_{w-1},x_{w-2},…,x_0]$和宽度位$w^{‘}$的位向量$\vec{x^{‘}}=[x_{w-1},…,x_{w-1},x_{w-1},x_{w-2},…,x_0]$,其中$w_{‘}>w$。
    • 则$B2T_w(\vec{x})=B2T_{w^{‘}}(\vec{x^{‘}})$
  • 当把short转换成unsigned时,要先改变大小,再完成有符号到无符号的转换


截断数字

  • 减少表示一个数字的数位时会发生截断
  • 从高位进行截断




整数运算


无符号加法

  • 若相加没有溢出,则正常相加运算
  • 若发生溢出,则进行高位截断
    • 对满足$0≤x,y<2^w$的x和y有:
  • 无符号数求反
    • 对满足$0≤x<2^w$的任意x,其w位的无符号逆元$-^{u}_{w}x$,由下式给出:

补码加法

  • 若相加没有溢出,则正常相加运算
  • 若发生溢出,则进行高位截断
    • 对满足$-2^{w-1}≤x,y≤2^{w-1}-1$的整数x和y,有:


补码的非

  • 对满足$TMin_w≤x≤TMax_w$的x,其补码的非$-_{w}^{t}x$为


无符号乘法

  • 对满足$0≤x,y≤UMax_w$的x和y有:


补码乘法

  • 仍然是相乘后高位截断
  • 对满足$TMin_w≤x,y≤TMax_w$的x和y有:


乘以常数

  • 整数乘法指令相当慢
  • 因此,编译器试着使用移位和加法运算的组合来代替乘以常数因子的乘法
  • 左移k位表示乘以$2^k$
  • 对固定字长左移k位时,其高k位被丢弃,相当于无符号乘法
  • 编译器会将x*14改写为(x<<3)+(x<<2)+(x<<1)或(x<<4)-(x<<1)


除以2的幂

  • 整数除法甚至比整数乘法更慢
  • 用右移和加法运算组合来代替
  • 无符号和补码数分别使用逻辑移位和算术移位来实现
  • 对x≥0,y>0,结果为$\lfloor x/y \lfloor$
  • 对x<0,y>0,结果$\lceil x/y \rceil$
  • 向下舍入一个正值,向上舍入一个负值
  • 无符号数直接进行右移即可,但补码数直接右移会导致向下舍入
    • 在执行算术右移之前加上一个适当的偏置量来导致结果正确舍入
    • C变量x和k分别有补码值x和无符号数值k,且0≤k< w,则当执行算数移位时,C表达式(x+(1<< k)-1)>>k产生数值$\lceil x/y \rceil$
  • 同乘法不同, 不能用除以2的幂的除法来表示除以任意常数k的除法




浮点数

  • 对形如V=$x \times 2^y$的有理数进行编码。
  • 用于非常大的数字(|V|>>0)、非常接近0(|V|<<1)的数字,以及实数运算的近似值计算


二进制小数

  • $b=b_{m}b_{m-1}…b_{1}.b_{-1}b_{-2}…b_{-n-1}b_{-n}$
  • $b=\sum_{i=-n}^{m}2^{i} \times b_{i}$


IEEE浮点表示

  • IEEE浮点标准用V=$(-1)^s \times M \times 2^E$的形式来表示一个数
    • 符号s决定这数是负数(s=1)还是正数(s=0),而对于数值0的符号位解释作为特殊情况处理
    • 尾数M是一个二进制小数,它的范围是1$\sim$-ε,或者是0$\sim$1-ε
    • 阶码E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。
    • 将浮点数的位表示划分为三个字段,分别对这些值进行编码:
      • 一个单独的符号位s直接编码符号s
      • n位小数字段frac=$f_{n-1}…f_1f_o$编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0
      • k位的阶码字段exp=$e_{k-1}…e_1e_0$编码阶码E
    • 根据exp的值,被编码的值可以分为三种不同的情况
      • 规格化的值
        • 当exp的位模式既不全为0,也不全为1时
        • E=e-Bias,e是无符号数,位表示为$e_{k-1}…e_1e_0$,Bias为一个等于$2^{k-1}-1$的偏置值
        • M=1+f,1≤M<2
      • 非规格化的值
        • 阶码域全为0时
        • E=1-Bias
        • M=f
        • 提供了一种表示数值0的方法
        • 由于符号位的存在,+0.0与-0.0被认为是不同的
        • 用于表示非常接近与0.0的数
        • 逐渐溢出:可能的数值分布均匀地接近于0.0
      • 特殊值
        • 阶码全为1时
        • 当小数域全为0时,得到的值表示无穷
        • s=0时为+∞,s=1时为-∞
        • 无穷能表示溢出的结果
        • 运算结果不能是实数或无穷,返回NaN(Not a Number)


数字示例


  • 此种表示法可以使用整数排序算法对浮点数进行排序


舍入

  • 浮点数无法准确表示,需要进行舍入
  • 四种舍入方法
    • 向偶数舍入
    • 向零舍入
    • 向下舍入
    • 向上舍入
  • 当数字不是两个可能值的正中间时,利用类四舍五入法判断使用向上舍入还是向下舍入
    • 如舍入到四分之一时,$10.00011_2$向下舍入为$10.00_2$,$10.00110_2$向上舍入为$10.01_2$
  • 当数字位于两个可能值的正中间时,采用向偶数舍入,舍得末位为0
    • 如舍入到四分之一时,$10.11100_2$向上舍入为$11.00_2$,$10.10100_2$向下舍入为$10.10_2$


浮点运算

  • 浮点加法可交换
    • x + y = y + x
  • 浮点加法不具有结合律
    • 3.14 + (1e10 - 1e10) = 3.14
    • (3.14 + 1e10) - 1e10 = 0
  • 浮点加法具有单调性
    • 如果x≥y,则对于任意x,y以及a的值(除了NaN),都有x+a≥y+a
    • 无符号或补码加法不具有此性质
  • 浮点乘法可交换
    • x * y = y * x
  • 浮点乘法不具有结合律
    • (1e20 * 1e20) * 1e-20 = +∞
    • 1e20 * (1e20 * 1e-20) = 1e20
  • 浮点乘法在加法上不具有分配性
    • 1e20 * (1e20 - 1e20) = 0.0
    • 1e20 * 1e20 - 1e20 * 1e20 = NaN
  • 浮点乘法具有单调性


C语言中的浮点数

  • float和double分别对应单精度和双精度浮点
  • 必须小心地使用浮点运算,注意整型和浮点型之间的转换!




《深入理解计算机系统》CSAPP(二)

https://gax-c.github.io/blog/2022/02/19/5_csapp_2/

Author

Gax

Posted on

2022-02-19

Updated on

2022-02-27

Licensed under

Comments