CSAPP:datalab

最近在看《深入理解计算机系统》,圣经确实是圣经,比我在学校理解计算机系统直接多了,简直没白看,真是可惜不早点知道有这本书,现在是赶鸭子般的啃书。所以我一直在搜会不会有什么看这本配套书的捷径,因为我自己看书实在太慢了。感觉没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!n0x0...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,就看看0xFF000000uf<<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;
}

你可能感兴趣的:(CSAPP:datalab)