深入理解计算机系统第二版家庭作业答案.docx
- 文档编号:28394965
- 上传时间:2023-07-13
- 格式:DOCX
- 页数:55
- 大小:44.77KB
深入理解计算机系统第二版家庭作业答案.docx
《深入理解计算机系统第二版家庭作业答案.docx》由会员分享,可在线阅读,更多相关《深入理解计算机系统第二版家庭作业答案.docx(55页珍藏版)》请在冰豆网上搜索。
深入理解计算机系统第二版家庭作业答案
深入理解计算机系统(第二版)家庭作业第二章
略
intis_little_endian(){
inta=1;
return*((char*)&a);
}
(x&0xFF)|(y&~0xFF)
unsignedreplace_byte(unsignedx,unsignedcharb,inti)
{
return(x&~(0xFF<<(i<<3)))|(b<<(i<<3));
}
A.!
~x
B.!
x
C.!
~(x>>((sizeof(int)-1)<<3))
D.!
(x&0xFF)
注意,英文版中C是最低字节,D是最高字节。
中文版恰好反过来了。
这里是按中文版来做的。
这里我感觉应该是英文版对的,int_shifts_are_arithmetic()
intint_shifts_are_arithmetic(){
intx=-1;
return(x>>1)==-1;
}
对于sra,主要的工作是将xrsl的第w-k-1位扩展到前面的高位。
这个可以利用取反加1来实现,不过这里的加1是加1<<(w-k-1)。
如果x的第w-k-1位为0,取反加1后,前面位全为0,如果为1,取反加1后就全是1。
最后再使用相应的掩码得到结果。
对于srl,注意工作就是将前面的高位清0,即xsra&(1<<(w-k)-1)。
额外注意k==0时,不能使用1<<(w-k),于是改用2<<(w-k-1)。
?
intsra(intx,intk){
intxsrl=(unsigned)x>>k;
intw=sizeof(int)<<3;
unsignedz=1<<(w-k-1);
unsignedmask=z-1;
unsignedright=mask&xsrl;
unsignedleft=~mask&(~(z&xsrl)+z);
returnleft|right;
}
intsrl(unsignedx,intk){
intxsra=(int)x>>k;
intw=sizeof(int)*8;
unsignedz=2<<(w-k-1);
return(z-1)&xsra;
}
intany_even_one(unsignedx){
return!
!
(x&());
}
inteven_ones(unsignedx){
x^=(x>>16);
x^=(x>>8);
x^=(x>>4);
x^=(x>>2);
x^=(x>>1);
return!
(x&1);
}
x的每个位进行异或,如果为0就说明是偶数个1,如果为1就是奇数个1。
那么可以想到折半缩小规模。
最后一句也可以是return(x^1)&1
intleftmost_one(unsignedx){
x|=(x>>1);
x|=(x>>2);
x|=(x>>4);
x|=(x>>8);
x|=(x>>16);
returnx^(x>>1);
}
位机器上没有定义移位32次。
变为2<<31。
C.定义a=1<<15;a<<=15;set_msb=a<<1;beyond_msb=a<<2;
感觉中文版有点问题,注释和函数有点对应不上,于是用英文版的了。
个人猜想应该是让x的最低n位变1。
intlower_one_mask(intn){
return(2<<(n-1))-1;
}
unsignedrotate_right(unsignedx,intn){
intw=sizeof(unsigned)*8;
return(x>>n)|(x<<(w-n-1)<<1);
}
这一题是看x的值是否在-2^(n-1)到2^(n-1)-1之间。
如果x满足这个条件,则其第n-1位就是符号位。
如果该位为0,则前面的w-n位均为0,如果该位为1,则前面的w-n位均为1。
所以本质是判断,x的高w-n+1位是否为0或者为-1。
intfits_bits(intx,intn){
x>>=(n-1);
return!
x||!
(~x);
}
A.得到的结果是unsigned,而并非扩展为signed的结果。
B.使用int,将待抽取字节左移到最高字节,再右移到最低字节即可。
intxbyte(unsignedword,intbytenum){
intret=word<<((3-bytenum)<<3);
returnret>>24;
}
是无符号整数,因此左边都会先转换为无符号整数,它肯定是大于等于0的。
B.判断条件改为if(maxbytes>0&&maxbytes>=sizeof(val))
请先参考题。
可知:
t=a+b时,如果a,b异号(或者存在0),则肯定不会溢出。
如果a,b均大于等于0,则t<0就是正溢出,如果a,b均小于0,则t>=0就是负溢出。
于是,可以利用三个变量来表示是正溢出,负溢出还是无溢出。
intsaturating_add(intx,inty){
intw=sizeof(int)<<3;
intt=x+y;
intans=x+y;
x>>=(w-1);
y>>=(w-1);
t>>=(w-1);
intpos_ovf=~x&~y&t;
intneg_ovf=x&y&~t;
intnovf=~(pos_ovf|neg_ovf);
return(pos_ovf&INT_MAX)|(novf&ans)|(neg_ovf&INT_MIN);
}
对于有符号整数相减,溢出的规则可以总结为:
t=a-b;
如果a,b同号,则肯定不会溢出。
如果a>=0&&b<0,则只有当t<=0时才算溢出。
如果a<0&&b>=0,则只有当t>=0时才算溢出。
不过,上述t肯定不会等于0,因为当a,b不同号时:
1)a!
=b,因此a-b不会等于0。
2)a-b<=abs(a)+abs(b)<=abs(TMax)+abs(TMin)=(2^w-1)
所以,a,b异号,t,b同号即可判定为溢出。
inttsub_ovf(intx,inty){
intw=sizeof(int)<<3;
intt=x-y;
x>>=(w-1);
y>>=(w-1);
t>>=(w-1);
return(x!
=y)&&(y==t);
}
顺便整理一下汇编中CF,OF的设定规则(个人总结,如有不对之处,欢迎指正)。
t=a+b;
CF:
(unsignedt)<(unsigneda)进位标志
OF:
(a<0==b<0)&&(t<0!
=a<0)
t=a-b;
CF:
(a<0&&b>=0)||((a<0==b<0)&&t<0)退位标志
OF:
(a<0!
=b<0)&&(b<0==t<0)
汇编中,无符号和有符号运算对条件码(标志位)的设定应该是相同的,但是对于无符号比较和有符号比较,其返回值是根据不同的标志位进行的。
根据2-18,不难推导,(x'*y')_h=(x*y)_h+x(w-1)*y+y(w-1)*x。
unsignedunsigned_high_prod(unsignedx,unsignedy){
intw=sizeof(int)<<3;
returnsigned_high_prod(x,y)+(x>>(w-1))*y+x*(y>>(w-1));
}
当然,这里用了乘法,不属于整数位级编码规则,聪明的办法是使用int进行移位,并使用与运算。
即((int)x>>(w-1))&y和((int)y>>(w-1))&x。
注:
不使用longlong来实现signed_high_prod(intx,inty)是一件比较复杂的工作,而且我不会只使用整数位级编码规则来实现,因为需要使用循环和条件判断。
下面的代码是计算两个整数相乘得到的高位和低位。
intuadd_ok(unsignedx,unsignedy){
returnx+y>=x;
}
voidsigned_prod_result(intx,inty,int&h,int &l){
intw=sizeof(int)<<3;
h=0;
l=(y&1)x:
0;
for(inti=1;i if((y>>i)&1){ h+=(unsigned)x>>(w-i); if(! uadd_ok(l,x< l+=(x< } } h=h+((x>>(w-1))*y)+((y>>(w-1))*x); } 最后一步计算之前的h即为unsigned相乘得到的高位。 sign_h=unsign_h-((x>>(w-1))&y)-((y>>(w-1))&x); sign_h=unsign_h+((x>>(w-1))*y)+((y>>(w-1))*x); A.K=5: (x<<2)+x B.K=9: (x<<3)+x C.K=30: (x<<5)-(x<<1) D.K=-56: (x<<3)-(x<<6) 先计算x>>k,再考虑舍入。 舍入的条件是x<0&&x的最后k位不为0。 intdivide_power2(intx,intk){ intans=x>>k; intw=sizeof(int)<<3; ans+=(x>>(w-1))&&(x&((1< returnans; } 这相当于计算((x<<2)+x)>>3,当然,需要考虑x为负数时的舍入。 先看上述表达式,假设x的位模式为[b(w-1),b(w-2),...,b(0)],那么我们需要计算: [b(w-1),b(w-2),b(w-3),...,b(0),0,0] +[b(w-1),b(w-2),...,b (2),b (1),b(0)] 最后需要右移3位。 因此我们可以忽略下方的b (1),b(0)。 于是就计算(x>>2)+x,再右移一位即是所求答案。 不过考虑到(x>>2)+x可能也会溢出,于是就计算(x>>3)+(x>>1),这个显然是不会溢出的。 再看看b(0)+b (2)会不会产生进位,如果产生进位,则再加一。 最后考虑负数的舍入。 负数向0舍入的条件是x<0&&((x<<2)+x的后三位不全为0)。 满足舍入条件的话,结果再加1。 容易证明,加法后三位不全为0可以等价为x后三位不全为0。 ? intmul5div8(intx){ intb0=x&1,b2=(x>>2)&1; intans=(x>>3)+(x>>1); intw=sizeof(int)<<3; ans += (b0&b2); ans += ((x>>(w-1))&&(x&7)); returnans; } 不懂题意,感觉就是。 A.1[w-n]0[n]: ~((1< B.0[w-n-m]1[n]0[m]: ((1< A.false,当x=0,y=TMin时,x>y,而-y依然是Tmin,所以-x>-y。 B.true,补码的加减乘和顺序无关(如果是右移,则可能不同)。 C.false,当x=-1,y=1时,~x+~y=0xFFFFFFFE,而~(x+y)==0xFFFFFFFF。 D.true,无符号和有符号数的位级表示是相同的。 E.true,最后一个bit清0,对于偶数是不变的,对于奇数相当于-1,而TMin是偶数,因此该减法不存在溢出情况。 所以左边总是<=x。 A.令x为无穷序列表示的值,可以得到x*2^k=Y+x。 所以x=Y/(2^k-1)。 B.(a)1/7,(b)9/15=3/5,(c)7/63=1/9 浮点数的一个特点就是,如果大于0,则可以按unsigned位表示的大小排序。 如果小于0则相反。 注意都为0的情况即可。 所以条件是: ((ux<<1)==0&&(uy<<1)==0)|| (! sx&&sy)|| (! sx&&! sy&&ux>=uy)|| (sx&&sy&&ux<=uy); A.,5表示为101,因此位数M就是为,小数f为=。 指数部分应该为E=2,所以其指数部分位表示为e=(2^(k-1)-1)+2=2^(k-1)+1。 位表示三个部分分别是s-e-f,为0-10..01-0100..0。 B.能被准确描述的最大奇数,那么其M=..1,故f部分全为1,E应该为n。 当然,这个假设在2^(k-1)>=n的情况下才能成立。 这时,s=0,e=n+2^(k-1)-1,f=11...1。 值为2^(n+1)-1。 C.最小的规格化数为2^(1-bias)即2^(-2^(k-1)+2),所以其倒数值V为2^(2^(k-1)-2),所以M为,f部分为全0,E=2^(k-1)-2,e部分为2^(k-1)-2+bias=2^k-3,即为11..101。 位表示为0-11..101-00..0。 描述 扩展精度 值 十进制 最小的正非规格化数 2^(-63)*2^(-2^14+2) 最小的正规格化数 2^(-2^14+2) 最大的规格化数 (2^64-1)*2^(2^14-1-63) +4932 描述 Hex M E V -0 0x8000 0 -62 -- 最小的值>1 0x3F01 257/256 0 257*2^(-8) 256 0x4700 1 8 -- 最大的非规格化数 0x00FF 255/256 -62 255*2^(-70) -inf 0xFF00 -- -- -- Hex为0x3AA0 0x3AA0 416/256 -5 416*2^(-13)=13*2^(-8) 格式A 格式B 位 值 位 值 101110001 -9/16 101100010 -9/16 010110101 208 011101010 208 100111110 -7/1024 100000111 -7/1024 000000101 6/2^17 000000000 0 111011000 -4096 111110000 -inf 011000100 768 011110000 inf 没有特别明白转换成最接近的,然后又说向+inf舍入的含义。 按理说,舍入到+inf就是向上舍入,而并不是找到最接近的。 表格中是按最接近的进行舍入,并且如果超出范围则认为是inf。 如果都按+inf进行舍入,那么第四行格式B将是000000001。 A.false,float只能精确表示最高位1和最低位的1的位数之差小于24的整数。 所以当x==TMAX时,用float就无法精确表示,但double是可以精确表示所有32位整数的。 B.false,当x+y越界时,左边不会越界,而右边会越界。 C.true,double可以精确表示所有正负2^53以内的所有整数。 所以三个数相加可以精确表示。 D.false,double无法精确表示2^64以内所有的数,所以该表达式很有可能不会相等。 虽然举例子会比较复杂,但可以考虑比较大的值。 E.false,0/为NaN,(非0)/为正负inf。 同号inf相减为NaN,异号inf相减也为被减数的inf。 float的k=8,n=23。 bias=2^7-1=127。 最小的正非规格化数为2^(1-bias-n)=2^-149。 最小的规格化数为2^(0-bias)*2=2^-126。 最大的规格化数(二的幂)为2^(2^8-2-bias)=2^127。 因此按各种情况把区间分为[TMin,-148][-149,-125][-126,127][128,TMax]。 floatfpwr2(intx) { /*Resultexponentandfraction*/ unsignedexp,frac; unsignedu; if(x<-149){ /*Toosmall.Return*/ exp=0; frac=0; }elseif(x<-126){ /*Denormalizedresult*/ exp=0; frac=1<<(x+149); }elseif(x<128){ /*Normalizedresult.*/ exp=x+127; frac=0; }else{ /*Toobig.Return+oo*/ exp=255; frac=0; } /*Packexpandfracinto32bits*/ u=exp<<23|frac; /*Returnasfloat*/ returnu2f(u); } 它表示的二进制小数值为: B.根据,可知1/7的表示为[001]..., 所以22/7为 C.从第9位开始不同。 为了方便测试,我写了几个公共函数。 typedefunsignedfloat_bits; floatu2f(unsignedx){ return*((float*)&x); } unsignedf2u(floatf){ return*((unsigned*)&f); } boolis_float_equal(float_bitsf1,floatf2){ returnf2u(f2)==f1; } boolis_nan(float_bitsfb){ unsignedsign=fb>>31; unsignedexp=(fb>>23)&0xFF; unsignedfrac=fb&0x7FFFFF; returnexp==0xFF&&frac! =0; } boolis_inf(float_bitsfb){ unsignedsign=fb>>31; unsignedexp=(fb>>23)&0xFF; unsignedfrac=fb&0x7FFFFF; returnexp==0xFF&&frac==0; } inttestFun(float_bits(*fun1)(float_bits),float(*fun2)(float)){ unsignedx=0; do{小数点右移不会到超过30次(否则就越界了),所以exp<=30。 而这里刚好用TMin来表示越界,因此不用关心TMin的表示。 深入理解计算机系统(第二版)家庭作业第三章 intdecode2(intx,inty,intz) { intret; z-=y; 8(%ebp)为result的地址。 12(%ebp)为。 16(%ebp)为。 B.栈中的内容如下表,分配的20个字节用黄底展示(每一行为4个字节) y x 返回地址 保存的ebp(也是当前ebp的指向) &s2(word_sum的返回值地址) 在汇编中,没懂word_sum15: ret$4 以及diff12: subl$4,%esp的意义何在。 可能是为了清除那个result的返回地址。 C. 传递结构体参数就像正常的传值。 结构体的每一个变量可以看做是单独的参数进行传入。 D. 返回结构体的通用策略: 将返回变量的地址看做第一个参数传入函数。 而不是在函数中分配栈空间给一个临时变量,因为eax确实存不下一个结构体,eax充当返回变量的指针的角色。 B取四的倍数的上整数=8。 8+4+(B*2)取四的倍数的上整数=28。 所以B的可选值为8和7。 2*A*B取四的上整数为44,所以A*B的可选值为21和22。 所以A=3,B=7。 我们用结构体A表示a_struct。 首先,根据第11和12行,可以得到CNT*size(A)=196。 根据13行,知道ecx+4*edx+8为ap->x[ap->idx]的地址。 ecx存储的是bp(地址)。 ap的地址是bp+4+i*size(A) 我们知道,ap->x[0]的地址是bp+4+i*size(A)+pos(x),pos(x)为结构体A中x的偏移。 那么ap->x[ap->idx]的地址是bp+4+i*size(A)+pos(x)+4*(ap->idx)。 所以4*edx+8=4+i*size(A)+pos(x)+4*(ap->idx)。 所以,不难猜测,pos(x)=4,也就是说,在A中,首先是idx,再是x数组。 那么,我们看ap->idx在哪里计算过。 到第10行,edx的结果是7i+bp[4+28*i], bp[4+28*i]是什么呢它很可能是bp中的a[i]的首地址。 我们先这样猜测,于是size(A)=28,并且bp[4+28*i]的值为ap->idx。 另一方面: 4*edx=28*i+4*bp[4+28*i]=i*size(A)+4*(ap->idx)。 所以,我们的猜想是正确的。 因此,size(A)=28,里面包含了一个intidx和一个数组intx[6]。 总共有多少个A呢CNT=196/size(A)=7。 A. : 0 : 4 : 0 : 4 B. 总共需要8个字节。 C. 不难知道,赋值前后都应该是整数。 edx就是参数up(一个指针)。 最后结果是用eax-(edx)得到的,说明(edx)是整数,即up->___为整数,肯定是表示的。 再看看之前的eax,eax是由(eax)所得,说明到第3行后,eax是个指针。 它是由(ecx)得到的,说明ecx在第二行也是个指针。 而ecx是通过*(up+4)得到的,所以ecx是一个union指针next,即up->; 到第三行,eax为*(ecx),且是一个指针,所以eax在第三行为int*p,即up->>。 所以,赋值符号后面的表达式就为*(up->>-up-> 再看看前面。 最终赋值的地址是ecx+4,而ecx那时候是一个next指针,而(next+4)必须是一个int,也不难推测它是。 因此前面就为up->>。 结果如下: voidproc(unionele*up) { up->>=*(up->>-up->; } 版本一: 使用getchar voidgood_echo() { charc; intx=0; while(x=getchar(),x! ='\n'&&x! =EOF) { putchar(x); } } 版本二: 使用fgets voidgoo
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入 理解 计算机系统 第二 家庭 作业 答案
![提示](https://static.bdocx.com/images/bang_tan.gif)