深入理解计算机系统(第三版)第二章重要内容摘要

第二章

2.1.3寻址和字节顺序

#include <stdio.h>
typedef unsigned char *byte_pointer;
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));
}
void show_float(float x){
    show_bytes((byte_pointer)&x,sizeof(float));
}
void show_point(void* x){
    show_bytes((byte_pointer)&x,sizeof(void*));
}
void test_show_bytes(int val){
    int ival=val;
    float fval=(float)val;
    int *pval=&ival;
    show_int(ival);
    show_float(fval);
    show_point(pval);
}
int main(){
    int i=12345;
    test_show_bytes(i);
    return 0;
}

2.2.3补码编码

有符号数的三种表示方法
补码:
原理:补码编码的定义
对向量\(\vec{x}=[x_{w-1},x_{w-2},\cdots,x_0]\)

$B2T_w(\vec{x})\doteq-x_{w-1}2^{w-1}+\sum_{i=0}^{w-2}x_i2^i$

反码:
反码(Ones'Complement):除了最高有效位的权是\(-(2^{w-1}-1)\)而不是\(-2^{w-1}\),它和补码是一样的:

$B2O_w(\vec{x})\doteq-x_{w-1}(2^{w-1}-1)+\sum_{i=0}^{w-2}x_i2^i$

原码:
原码(Sign-Magnitude):最高有效位是符号位,用来确定剩下的位应该取负权还是正权:

$B2S_w(\vec{x})\doteq(-1)x_{w-1}\cdot\sum_{i=0}^{w-2}x_i2^i$

2.3.3补码的非

原理:补码的非
对满足\(TMin_x\leq x \leq TMax_w\)\(x\),其补码的非\(-_w^tx\)由下式给出
\(-_w^tx=\begin{cases} TMin_w,&x = TMin_w\\ -x,&x>TMin_w \end{cases}\)
执行位级补码非得第一种方法是对每一位求补,再对结果加1。
 计算一个数\(x\)的补码非得第二种方法是建立在将位向量分为两部分的基础之上的。假设\(k\)是最右边的1的位置,因而\(x\)的位级表示形如\([x_{w-1},x_{w-2},\cdots,x_{k+1},1,0,\cdots,0]\)。(只要\(x\neq0\)就能够找到这样的\(k_0\)。)这个值的非写成二进制格式就是\([\sim{x}_{w-1},\sim x_{w-2},\cdots,\sim x_{k+1},1,0,\cdots,0]\)。也就是,我们对位\(k\)左边的所有位取反。

2.3.7除以2的幂

 回到\(y=2^k\)的情况,C表达式\(x+(1\ll{k})-1\)得到数值\(x+2^k-1\)。将这个值算术右移\(k\)位即产生\(\lfloor{x/2^k}\rfloor\)
 这个分析表明对于使用算术右移的补码机器,C表达式
\((x<0?x+(1\ll{k})-1:x)\gg k\)

2.4.2IEEE浮点表示

 IEEE复变标准用\(V=(-1)^s\times M\times 2^E\)的形式来表示一个数:

  • 符号(sign) \(s\)决定这个数是负数(\(s=1\))还是正数(\(s=0\)),而对于数值0的符号位解释作为特殊情况处理。
  • 尾数(significand) \(M\)是一个二进制小数,它的范围是\(1\sim2-\epsilon\),或者是\(0\sim1-\epsilon\)
  • 阶数(exponent) E的作用是对浮点数加权,这个权重是2的E次幂(可能是负数)。
    将浮点数的位表示划分为三个字段,分别对这些值进行编码:
  • 一个单独的符号位\(s\)直接编码符号\(s\)
  • \(k\)位的阶码字段exp=\(e_{k-1}\cdots e_1e_0\)编码阶码E。
  • \(n\)位小数字段frac=\(f_{n-1}\cdots f_1f_0\)编码尾数M,但是编码出来的值也依赖于阶码字段的值是否等于0。
     图给出了将这三个字段装进字中两种最常见的格式。在单精度浮点格式(C语言中的float)中,s、exp和frac字段分别为1位、\(k\)=8位和\(n\)=23位,得到一个32位的表示。在双精度浮点格式(C语言中的double)中,s、exp和frac字段分别为1位、\(k\)=11位和\(n\)=52位,得到一个64位的表示。

单精度

s exp frac
双精度
s exp frac(51:32)
frac(31:0)
标准浮点格式(浮点数由3个字段表示。两种最常见的格式是它们被封装到32位(单精度)和64位(双精度)的字中)

可以观察到最大非规格化数\(\frac{7}{512}\)和最小非规格化数\(\frac{8}{512}\)之间的平滑转变。这种平滑性归功于我们对非规格化数的\(E\)的定义。通过将\(E\)定义为\(1-Bias\),而不是\(-Bias\),我们可以补偿非规格化数的尾数没有隐含的开头的1。

描述 exp frac 单精度 双精度
十进制 十进制
0 00...00 0...00 0 0.0 0 0.0
最小非规格化数 00...00 0...01 $2^{-23}\times2^{-126}$ $1.4\times10^{-45}$ $2^{-52}\times2^{-1022}$ $4.9\times10^{-324}$
最大非规格化数 00...00 1...11 $(1-\epsilon)\times2^{-126}$ $1.2\times10^{-38}$ $(1-\epsilon)\times2^{-1022}$ $2.2\times10^{-308}$
最小规格化数 00...01 00...00 $1\times2^{-126}$ $1.2\times10^{-38}$ $1\times2^{-1022}$ $2.2\times10^{-308}$
1 01...11 0...00 $1\times2^0$ $1.0$ $1\times2^0$ $1.0$
最大规格化数 11...10 1...11 $(2-\epsilon)\times2^{127}$ $3.4\times10^{38}$ $(2-\epsilon)\times2^{1023}$ $1.8\times10^{308}$
  • \(+0.0\)总有一个全为0的位表示。
  • 最小的正非规格化值的位表示,是由最低有效位为1而其他所有位为0构成的。它具有小数(和尾数)值\(M=f=1-2^{-n}\)和阶码值\(E=-2^{k-1}+2\)。因此它的数字值是\(V=2^{-n-2^{k-1}+2}\)
  • 最大的非规格化值的位模式是由全为0的阶码字段和全为1的小数字段组成的。它有小数(和尾数)值\(M=f=1-2^{-n}\)(我们写成\(1-\epsilon\))和阶码值\(E=-2^{k-1}+2\)。因此,数值\(V=(1-2^{-n})\times2^{-2^{k-1}+2}\),这仅比最小的规格化值小一点。
  • 最小的正规格化值的位模式的阶码字段的最低有效位为1,其他位全为0。它的尾数值\(M=1\),而阶码值\(E=-2^{k-1}+2\)。因此,数值\(V=2^{-2^{k-1}+2}\)

  值1.0的位表示的阶码字段除了最高有效位为0之外,其它位都等于1。它的尾数值M=1,而它的阶码值E=\(2^{k-1}-2^{k-1}\)=0

  • 最大的规格化值的位表示的符号位为0,阶码的最低有效位等于0,其他位等于1。它的小数值\(f=1-2^{-n}\),尾数\(M=2-2^{-n}\)(我们写作\(2-\epsilon\))。它的阶码值\(E=2^{k-1}-1\),得到数值\(V=(2-2^{-n})\times2^{2^{k-1}-1}=(1-2^{-n-1})\times2^{2^{k-1}}\)

2.4.4舍入

方式 1.40 1.60 1.50 2.50 -1.50
向偶数舍入 1 2 2 2 -2
向零舍入 1 1 1 2 -1
向下舍入 1 1 1 2 -2
向上舍入 2 2 2 3 -1

在我们不想舍入到整数时,也可以使用向偶数舍入。我们只是简单地考虑最低有效数字是奇数还是偶数。例如,假设我们想将十进制数舍入到最接近的百分位。不管用哪种舍入方式,我们都将把1.2349999舍入到1.23,而将1.2350001舍入到1.24,因为它们不是在1.23和1.24的正中间。另一方面我们将把两个数1.2350000和1.2450000都舍入到1.24,因为4是偶数。

相似地,向偶数舍入法能够运用在二进制小数上。我们将最低有效位的值0认为是偶数,值1认为是奇数。一般来说,只有形如\(XX\cdots X.YY\cdots Y100\cdots\)的二进制位模式的数,这种舍入方式才有效,其中\(X\)\(Y\)表示任意位值,最右边的Y是要被舍入的位置。只有这种位模式表示在两个可能的结果正中间的值。例如,考虑舍入值到最近的四分之一的问题(也就是二进制小数点右边2位)。我们将\(10.00011_2(2\frac{2}{32})\)向下舍入到\(10.00_2(2)\),将\(10.00110_2(2\frac{3}{16})\)向上舍入到\(10.01_2(2\frac{1}{4})\),因为这些值不是两个可能值的正中间值。我们将\(10.11100_2(2\frac{7}{8})\)向上舍入到\(11.00_2(3)\),而\(10.10100_2(2\frac{5}{8})\)向下舍入到\(10.10_2(2\frac{1}{2})\),因为这些值是两个可能值的中间值,并且我们倾向于使最低有效位为零。

2.4.5浮点运算

浮点加法不具有结合性
另一方面,浮点加法满足了单调性属性:如果\(a\geq b\),那么对于任何\(a\)\(b\)以及\(x\)的值,除了\(NaN\),都有\(x+a\geq x+b\)。无符号或补码加法不具有这个实数(和整数)加法的属性。

浮点乘法不具有结合性和分配性

2.4.6C语言中的浮点数

当在int、float和double格式之间进行强制类型转换时,程序改变数值和位模式的原则如下(假设int是32位的):
  • 从int转换成float,数字不会溢,但是可能被舍入。
  • 从int或float转换成double,因为double有更大的范围(也就是可表示值的范围),也有更高的精度(也就是有效位数),所以能够保留精确的数值。
  • 从double转换成float,因为范围要小一些,所以值可能溢出成 \(+\infty\)\(-\infty\)。另外,由于精确度较小,它还可以被舍入。
  • 从float或者double转换成int,值将会向零舍入。例如,1.999将被转换成1,而-1.999将被转换成-1.进一步来说,值可能会溢出。C语言标准没有对这种情况指定固定的结果。与Intel兼容的微处理器指定位模式\([10\cdots00]\)(字长为\(\omega\)时的\(TMin_w\))为整数不确定(integer indefinite)值。一个从浮点数到整数的转换,如果不能为该浮点数找到一个合理的整数近似值,就会产生这样一个值。因此,表达式(int)+1e10会得到-21483648,即从一个正值变成了一个负值。

第一章课后练习

1.1

  A  加速比\(S=T_{old}/T_{new}=\frac{1}{(1-a)+\frac{a}{k}}=\frac{1}{(1-0.6)+\frac{0.6)}{1.5)}}=1.25\)
  B  \(S=T_{old}/T_{new}=\frac{1}{(1-a)+\frac{a}{k}}\)可推出\(1.67=\frac{1}{(1-0.6)+\frac{0.6}{k}} \approx3\)即3*100=300公里每小时

1.2

  \(2=\frac{1}{(1-0.8)+\frac{0.8}{k}} \approx2.66\)

第二章课后练习

练习2.1

  A  \(0x39A7F8\rightarrow001110011010011111111000\)
  B  \(1100100101111011\rightarrow0xC97B\)
  C  \(0xD5E4C\rightarrow11010101111000101100\)
  D  \(1001101110011110110101\rightarrow0x26E7B5\)

练习2.2

n \(2^n(十进制)\) \(2^n(十六进制)\)
9 512 0x200
19 524288 0x80000
14 16384 0x4000
16 65536 0x10000
17 131072 0x20000
5 32 0x20
7 128 0x80

练习2.3

十进制 二进制 十六进制
0 00000000 0x00
167 10100111 0xA7
62 00111110 0x3E
188 10111100 0xBC
55 00110111 0x37
136 10001000 0x88
243 11110011 0xF3
82 01010010 0x52
172 10101100 0xAC
247 11100111 0xE7

练习2.4

  A  0x503c+0x8=0x5044
  B  0x503c-0x40=0x4ffc
  C  0x503c+64=0x503c+0x40=0x507c
  D  0x50ea-0x503c=0x50bd

练习2.5

int main(){
	int i=0x87654321;
	byte_pointer valp=(byte_pointer)&i;
	show_bytes(valp,2);
	return 0;
}

  A
    小端法:21
    大端法:87
  B  
    小端法:2143
    大端法:8765
  C
    小端法:214365
    大端法:876543

练习2.6

  A
    \(0x00359141\rightarrow001101011001000101000001\)
    \(0x4A564504\rightarrow01001010010101100100010100000100\)
  B
       001101011001000101000001
    01001010010101100100010100000100
    总共有21位匹配
  C
    串的头尾不匹配

练习2.7

int main(){
const char *s="abcdef";
show_bytes((byte_pointer)s,strlen(s));
return 0;
}

  616263646566

练习2.8

运算 结果
a [01101001]
b [01010101]
~a [10010110]
~b [10101010]
a&b [01000001]
a|b [01111101]
a^b [00111100]

练习2.9

R G B 颜色 补集
0 0 0 黑色 白色
0 0 1 蓝色 黄色
0 1 0 绿色 红紫色
0 1 1 蓝绿色 红色
1 0 0 红色 蓝绿色
1 0 1 红紫色 绿色
1 1 0 黄色 蓝色
1 1 1 白色 黑色

蓝色|绿色=蓝绿色
黄色&蓝绿色=绿色
红色^红紫色=蓝色

练习2.10

void inplace_space(int *x,int *y){
*y=*x^*y;/* Step1 */
*x=*x^*y;/* Step2 */
*y=*x^*y;/* Step3 */
}

每个元素就是它自身的加法逆元(a^a=0)

步骤 *x *y
初始 a b
第一步 a a^b
第二步 b a^b
第三步 b a

练习2.11

  A 变量first和last都为k+1
  B 由上图可知第一步时last值就为0(a^a)了
  C 去掉=即可

void reverse_array(int a[],int cnt){
for(int first=0,last=cnt-1;first<last;inplace_space(&a[first++],&a[last--]));
}

练习2.12

  A x&0xFF
  B x^~0xFF
  C x|0xFF

练习2.13

  \(bis(x,y)\)
  \(bis(bic(x,y),bic(y,x))\rightarrow\) x^y=(x&y)|(x&y)

posted @ 2021-01-08 10:18  丸子球球  阅读(223)  评论(0编辑  收藏  举报