最近在看《深入理解计算机系统》,圣经确实是圣经,比我在学校理解计算机系统直接多了,简直没白看,真是可惜不早点知道有这本书,现在是赶鸭子般的啃书。所以我一直在搜会不会有什么看这本配套书的捷径,因为我自己看书实在太慢了。感觉没2,3个月都不能吃完这本书。逼乎上很多说可以看CMU的视频,可是我自己本身英文算不上特别好,本来理解这东西已经有一定难度,如果再加上英文可能就痛不欲绝,简直更慢,不过也是这次我搜索的机缘,发现原来CMU给每一章都有一个配套练习。。orz,感觉上还是脚踏实地蹲完这本书会更好点,再做练习巩固一下,最快的捷径就是没有捷径了。
废话不多说,先上我做的第一章实验,同时总结一下,以便我以后回顾。做这套题确实煎熬,我花了两天时间,部分题目我也是参考了别人的想法我才能做的出来,实在是太难了,真佩服CMU的本科生,果然是真的牛,所以说考上一个好学校是多么多么多么重要,有牛逼的老师,有牛逼的同学,你才会更牛逼。
http://csapp.cs.cmu.edu/3e/labs.html
一,bitAnd
题目:只能用~和|来实现位的与操作。
感想:这道题比较简单,稍微思考一下就可以出来了。
想法:a&b
操作 代表 a和b 共同拥有的1(位置必须相同),那么我们反过来思考,~a
就是a不拥有的1,~b
就是b不拥有的1,合起来~a|~b
就是a和b都不拥有的1,那么~(~a|~b)
不就是a和b都拥有的1了。当然也可以向交集这样方向想,更清晰点。
/*
* bitAnd - x&y using only ~ and |
* Example: bitAnd(6, 5) = 4
* Legal ops: ~ |
* Max ops: 8
* Rating: 1
*/
int bitAnd(int x, int y)
{
int temp=~(~x|~y);
return temp;
}
二,getByte
题目:给定n (0<=n<=3),求出第n个字节是哪数字。
感想:开始增加一点难度,其实难度也不大就是考定理,刚开始我的想法就是将0xFF
,移动n个字节相&一下就是答案,可是我想着n要用上乘号*
才行,但题目是只能用下! ~ & ^ | + << >>
的操作符,我根本没办法去确认乘号,明显钻牛角尖了。n<<3
就是 n*8 实际就是n*(2^3)
。我真是脑残。。
想法:基本想法用0xFF
移动n个字节,即n<<3
,与输入数字相&。
/*
* getByte - Extract byte n from word x
* Bytes numbered from 0 (LSB) to 3 (MSB)
* Examples: getByte(0x12345678,1) = 0x56
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 6
* Rating: 2
*/
int getByte(int x, int n) {
int tmp = x >> ( (n) << 3 );
tmp =tmp & 0xFF;
return tmp;
}
三,logicalShift
题目:将x按逻辑位移移动n(0<=n<=31) 位。
感想:难度更深一层了,这道题目也让我想了很久我才做出来,做这些题目简直看感觉,感觉来了敲敲脑袋就出来了。
想法:基本思路就是如例子,如果我将x移动n=4
位,将结果&上0x0FFFFFFF
,就是答案了,那如何构造这个32-n
的1呢?首先我们看看范围n<=31的,那么我们肯定能找到n的相反数(~n)+1 既 -n
,这样就出来了减号-
了啊,那么我们不就知道了tmpn=32-n
是多少之后,我们不就可以通过(1<
0x0...FFF
了,可是你会突然发现当n=0的时候,就会构造出0x0...000
,没移动应该构造是0xF...FFF
,我们既要在n>0时不影响已经构造出来的0x0...FFF
,最好n==0可以加上0xF...FFF
,加个判断不就好了?那怎么加?这个地方很巧妙,!n
先判断n是否为0,如果是n>0,!n
是0x0...000
取反则为0xF...FFF
再加上0x1
刚好是0x0
对结果一点影响也没有,同理你会发现n==0这个( (~(!n))+1)
,判断就是0xF...FFF
,就是这样的小技巧就能得到正确的构造。
/*
* logicalShift - shift x to the right by n, using a logical shift
* Can assume that 0 <= n <= 31
* Examples: logicalShift(0x87654321,4) = 0x08765432
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int logicalShift(int x, int n)
{
int tmpn = 32 +( (~n) +1 );
int tmp = (1<< tmpn)+(~0) + ( (~(!n))+1);
int ans = (x >> n) & tmp;
return ans ;
}
四,bitCount
题目:用位运算计算出x中有多少个1
感想:巨难题,反正给我想,我怎么想也想不出来,实在太有意思了,可能还是我对二进制不敏感。我也是参考别人的做出来的。
想法:利用分治的思想。
int bitCount(int x)
{
int count;
int tmpMask1=(0x55) | (0x55 << 8);
int mask1 = (tmpMask1) | (tmpMask1 << 16); //得到掩码 01010101...01010101
int tmpMask2 = (0x33) | (0x33 << 8);
int mask2 = (tmpMask2) | (tmpMask2 << 16); //得到掩码 00110011...00110011
int tmpMask3 = (0x0f) | (0x0f << 8);
int mask3 = (tmpMask3) | (tmpMask3 << 16);//得到掩码 00001111...00001111
int mask4 = (0xff) | (0xff <<16); //得到掩码 0000 0000 1111 1111 0000 0000 1111 1111
int mask5 = (0xff) | (0xff << 8);//得到掩码: 0000 0000 0000 0000 1111 1111 1111 1111
count = (x & mask1) + ((x >> 1) &mask1);
count = (count & mask2) + ((count >> 2) & mask2);
count = (count & mask3) + ((count >> 4) & mask3);
count = (count & mask4) + ((count >> 8) & mask4);
count = (count & mask5) + ((count >> 16) & mask5);
return count;
}
五,bang
题目:不能!运算符求出!x结果
感想:想这个题目的时候总是脑袋抽筋,想着相反数~x
,右移1位与x
相&就知道是不是有1了,这是脑抽想出来的,根本不可能,也没什么论证,最后参考了别人的代码,茅塞顿开。还是一句话,灵感。
想法:我们得知道一个特性就是0x0...000 | ((~0x0...000)+1)
还是等于0x0...000
,但是其他任何数自己|
上自己的相反数最高位一定是1,这就是关键点了。很容易吧?然后把最高位右移31位来判断是否为0即可。
/*
* bang - Compute !x without using !
* Examples: bang(3) = 0, bang(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int bang(int x)
{
//int tmpx = x & ( (~x) >> 1);
int tmpx = x | ( (~x)+1);
tmpx = tmpx >> 31;
tmpx = tmpx +1;
return tmpx;
}
六,tmin
题目:返回补码整数的最小整数数值。
感想:安慰题,送给你爽一把。
想法:0x80000000
就是补码整数的最小值了啊。
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void)
{
int ans=(1<<31);
return ans;
}
七,fitsBits
题目:只给出n个二进制位,能否表示x。
感想:这道题不难,不过我也花了些时间,我在纸上画了一堆数才找到的破绽。
想法:有两个规律假设n=4只有当0x11111...[1xxx]
或0x00000...[0xxx]
我们才能用n个二进制位来表式x,想法出来了我就急将x往右移n-1位~((~n)+1)
,判断剩下的数字是不是全1或者全0不就好了啊?是的就是这样辣。
/*
* fitsBits - return 1 if x can be represented as an
* n-bit, two's complement integer.
* 1 <= n <= 32
* Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int fitsBits(int x, int n)
{
int tmp = ~((~n)+1);
int tmpx = x >> tmp;
int ans = ( !tmpx | !(tmpx+1) );
return ans;
}
八,divpwr2
题目:给出整数x,整数n,求[x/(2^n)],答案要接近趋向0方向。
感想:这道题其实也不算是特别难,写些数字研究一下就有思路了,利用发现的眼睛
想法:一看公式我立马联想到getByte,道理不是一样吗,不就是x>>n
位,这么简单,方向是没错的,可是这么简单就不会出这道题了,例如,假设只有4个二进制位,-5(0x1011)
我要右移1位-3(0x1101)
,这就错了啊,我们还可以发现其实所有的负数都向下取整了,那么该如何对负数向上取整呢?就是加一个偏置值啊,比方说(-5+[2^1-1])/(2^1)
,不就可以了吗?答案就出来了。我们先判断是不是负数tmp=(~( (x >> 31) & 0x1) )+1
,是负数构造出0xF...FFF
出来,然后构造q=2^n-1 既~((~0)<
(tmp & q) 既 0x0...FFF
,这就和我们的思想一模一样,再右移动n位。
/*
* divpwr2 - Compute x/(2^n), for 0 <= n <= 30
* Round toward zero
* Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int divpwr2(int x, int n)
{
int tmp = (~( (x >> 31) & 0x1) )+1 ;
int q= ~((~0)<> n ;
return ans;
}
九,negate
题目:给定x求-x。
感想:送分题。
想法:定理x的相反数是(~x)+1
,计算机的人都知道。
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x)
{
int ans=(~x)+1;
return ans;
}
十,isPositive
题目:判断x是不是正数
感想:再来一个安慰题。
想法:简单想法,先判断是不是负数,或者说是0,如果是其中一种情况就不是大于0的数,瞬间解决。
/*
* isPositive - return 1 if x > 0, return 0 otherwise
* Example: isPositive(-1) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 8
* Rating: 3
*/
int isPositive(int x)
{
int tmp = (x>>31) & 0x1;
int ans = !( tmp | !x );
return ans;
}
十一,isLessOrEqual
题目:用位运算判定x<=y,如果是就返回1,如果不是就返回0。
感想:折寿了,这道题我基本把关键点想出来,我总在想通过0<=y-x
,来进行判断,我已经知道如果x是负数,y正数,肯定是返回1的情况,然后想同号又怎么办?那就用y-x
判断啊,但我感觉异号又可以用这种情况来判断,可是会溢出啊,这又怎么办,我实在很蛋疼啊。然后参考了下别人答案, 我特么脑抽了吧。不就是两种情况分开嘛。我合起来干嘛,我在想啥。。
想法:入口点都很容易能想出来y-x >= 0
,但是我们会发现这样的问题就是y-x
肯定是会溢出的,为了防止这种情况发生,就应该分别分两种情况同号和异号。同号:,我们就判断y-x
的符号是不是=0的情况! ( ( (~x)+1+y ) >> 31)
。异号:我们就判断x的符号是不是-
即可。这样就避免的溢出。
/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y)
{
int signx = (x >> 31) &0x1;
int signy = (y >> 31) &0x1;
int isSameSign=! (signx ^ signy) ;
int p=! ( ( (~x)+1+y ) >> 31);
int ans = ( isSameSign & p ) | ( ( !isSameSign ) & signx);
return ans ;
}
十二,ilog2
题目:求整数的log(x)。
感想:这道题我是想不出来怎么做了,我只有一个简单的入口想法就是获取最高位的1在哪个位置,就是答案。但是我并不知道该怎么操作,下面这个想法很神奇,最难题之二。
想法:想法是二分的思想,公式log(x)=16a+8b+4c+2d+e。那么count=abcde。因为x长32位,首先我们先将x>>16
,判断高16位是不是还>0,如果>0,!(x>>16)
就是0,我们要将他转换到a的位置就是将! !(x>>16)
再次取非是1,然后<<4
,到a的位置,就说明这个数大于16,1肯定在高16位处,然后在接着将高位折半到8位,就是>>8+16
,看看高8位是不是也是>0
。这样一步步下去就是答案了。发现会很神奇这样的操作。
/*
* ilog2 - return floor(log base 2 of x), where x > 0
* Example: ilog2(16) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int ilog2(int x)
{
int count = 0;
count = (!!(x>>16)) << 4;
count =count+((!!(x>>(8 + count)))<<3);
count =count+((!!(x>>(4 + count)))<<2);
count =count+((!!(x>>(2 + count)))<<1);
count =count+((!!(x>>(1 + count)))<<0);
return count;
}
十三,float_neg
题目:就是返回输入uf的负数形式-uf。如果uf是NAN形式就直接返回参数。
感想:送分。
想法:题目很简单的了。要你熟记浮点数的编码形式。浮点数是由s(1位)E(8位)M(23位)(-1)^s * M * 2^E
组成的浮点数,当E是0xFF
时,要么这个数是inf或NAN
,可inf
也很很特殊就是M就是全0的情况所以我们捉住这个特殊点,先把uf<<1
忽略了s,就看看0xFF000000
在uf<<1
的位置是不是也是0xFF000000
,如果是再判断一下uf是不是就等于0xFF000000
就代表他原先就是个inf
数,如果不等于0xFF000000
就代表他原先就是个NAN
数。按情况加上个0x80000000
即可了啊。
/*
* float_neg - Return bit-level equivalent of expression -f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representations of
* single-precision floating point values.
* When argument is NaN, return argument.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 10
* Rating: 2
*/
unsigned float_neg(unsigned uf)
{
unsigned x=0x80000000;
unsigned wu=0xFF000000;
unsigned tmp = uf<<1;
if( (wu&tmp ) == wu)
{
if(tmp != wu) return uf;
}
return uf^x;
}
十四,float_neg
题目:将int型的x转为float型的x。
感想:这道题也不难,虽然说是rating4的题,就是很麻烦而已。
想法:这道题我就不讲了,太基础了。参考中文版书P82页的整数转浮点数还有P83页的页的向偶数舍入。验证有没有认真看书的时候到了。
/*
* float_i2f - Return bit-level equivalent of expression (float) x
* Result is returned as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point values.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_i2f(int x)
{
unsigned ans;
int tmpx=x;
int f=0;
int delta=0;
int tail=0;
int E=0;
if(x == 0) return x;
if(x == 0x80000000) return 0xcf000000;
ans=x&0x80000000;
if(ans) tmpx = -x;
while( (tmpx>>E)) E++;
E = E -1;
tmpx = tmpx<<(31-E);
tail = (tmpx >> 8) & 0x007FFFFF;
f = tmpx & 0xff;
delta =(f>128) || ( (f == 128) && (tail & 1) );
tail+=delta;
E=E+127;
if(tail >> 23)
{
tail = tail &0x007FFFFF;
E+=1;
}
ans=ans | E << 23 | tail;
return ans;
}
十五,float_twice
题目:就是将浮点数乘以2倍。
感想:不难,可以用datalab提供的工具fshow尝试下两倍的效果,总结一下就是答案了。
想法:按照浮点数想法先把浮点数拆成s(1位)E(8位)M(23位)
,然后我们先把NAN
情况去除之后,当E是0时M<<1
。当M是0时E<<1
,最后将3个部分加起来,就是酱紫了喵!
/*
* float_twice - Return bit-level equivalent of expression 2*f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representation of
* single-precision floating point values.
* When argument is NaN, return argument
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_twice(unsigned uf)
{
unsigned S=uf&0x80000000;
unsigned E=uf&0x7F800000;
unsigned M=uf&0x007FFFFF;
unsigned wu = 0xFF000000;
unsigned tmp = uf << 1;
unsigned ans = uf;
if( ( wu & tmp ) == wu)
{
return uf;
}
if(E != 0)
{
ans=S+(E+0x00800000)+M;
}
else if( M!=0 )
{
ans=S+E+(M << 1);
}
return ans;
}