CMU 15213 深入理解计算机系统 lab 1 - data lab

直入主题,13道bit运算puzzle,其中收获最大的是howManyBitsfloatFloat2Int

  1. 用~和&实现^异或。
    思路:可以分成四种情况,(1,0), (1,1), (0,0), (0,1),我们发现,需要将位上不相同的数保留为1,相同的变成0,而&操作是两个数同为1时才是1,否则是0,于是对于不相同的数,我们想到了先取&,然后取反~,自然都会变成1,对于相同的数,如果执行同样操作,那么只有(1,1)会变成0,而(0,0)会变成1,但我们希望他们都变成0,所以我们想到了把两个结果用&组合一下,但是如果保持同样操作不变地组合,(0,0)的结果还是1,所以我们可以对于其中一边先同时取反,这样可以保证两个相同的数做操作,总有一边是0,另一边是1,这样继续取一次&,就可以了。而对于不同的数同时取反,异或结果不变
/* 
 * bitXor - x^y using only ~ and & 
 *   Example: bitXor(4, 5) = 1
 *   Legal ops: ~ &
 *   Max ops: 14
 *   Rating: 1
 */
int bitXor(int x, int y) {
  return ~(~x & ~y) & ~(x & y)
}
  1. int 4字节,TMin最高位1,其余为0,没啥好说的
/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
  return 1 << 31
}
  1. 判断是否TMax。其实,当且仅当x=TMax或-1时,才有x+1=~x。知道这个就好算了,先判断是否为TMax or -1,只有这两种情况异或为0,然后再除去-1(也就是1111)的情况,这里要知道,只有0000取非!是1,其他值取非都是0
/*
 * isTmax - returns 1 if x is the maximum, two's complement number,
 *     and 0 otherwise 
 *   Legal ops: ! ~ & ^ | +
 *   Max ops: 10
 *   Rating: 1
 */
int isTmax(int x) {
  //x = TMax or -1
  int x1 = x+1;
  int x2 = ~x;
  int xor = x1^x2; //0000

  //see if x = -1
  int y1 = ~x; //0000
  int res1 = !y1; //1

  return !(xor | res1);
}
  1. 判断一个数是否在奇数位上全是1。可构建mask 10101010...,然后按位&看结果是否与mask相等。由于不能用==判断,我们可以把结果与mask做异或然后取反。需要注意的是,lab1限制定义int为1字节,所以不能直接写int mask = 0xAAAAAAAA,需要做一些左移操作
/* 
 * allOddBits - return 1 if all odd-numbered bits in word set to 1
 *   where bits are numbered from 0 (least significant) to 31 (most significant)
 *   Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 2
 */
int allOddBits(int x) {
  int a = 0xAA;
  int b = (a << 8) + a;
  int mask = (b << 16) + b;
  int and = x & mask; //should == mask

  return !(and ^ mask);
}
  1. 取负 = 取反 + 1,书上写了
/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
  return ~x + 1;
}
  1. 首先判断高4位是否为0011, 再判断低四位是否在0000和1001之间。可以看到,如果低4位以0开头,则后3位可为任意值;如果以1开头,那么接下来两位也必须是0,可写为
/* 
 * isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
 *   Example: isAsciiDigit(0x35) = 1.
 *            isAsciiDigit(0x3a) = 0.
 *            isAsciiDigit(0x05) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 3
 */
int isAsciiDigit(int x) {
  int left = !((x >> 4) ^ 3); //1 if 0x3.

  int r1 = (x & 2) >> 1;
  int r2 = (x & 4) >> 2;
  int r3 = (x & 8) >> 3;

  int right1 = !r2 & !r1; //r3 = 1
  int right2 = !r3; //r3 = 0
  int right = right1 | right2;

  return left & right;
}
  1. 如果x=0,return z,其他情况return y。
    思路: 可知最终结果要么是y要么是z,所以他俩必须出现在同一个表达式中,而且需一个数与1111做&,另一个数与0000做&。剩下的就是怎么把1或0转成1111和0000了
/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */
int conditional(int x, int y, int z) {
  int flag = !!x; //if x != 0, 1, else 0
  int f = ~flag + 1; //convert to 1111 or 0000

  return (f & y) | (~f & z);
}
  1. 如果x<=y, return 1, else return 0。主要就是分x和y是否符号相同来判断。
/* 
 * 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) {
  //cond 1, same sign, x <= y
  int xhighest = (x >> 31) & 1;
  int yhighest = (y >> 31) & 1;
  int samesign = !(xhighest ^ yhighest);

  int diff = ~x + 1 + y;
  int xlessy = !((diff >> 31) & 1);

  int res1 = samesign & xlessy;

  //cond 2, not same sign, x < 0
  int xlesszero = xhighest;
  int res2 = (!samesign) & xlesszero;

  return res1 | res2;
}

9.实现logicalNeg,!0=1, !Others=0。
思路:参考了一下别人的,除了0,其他数其补码(取反+1)再和自己做或(|)操作得出的最高位始终为1。0做同样操作最高位为0,然后右移31位+1就可。注意,是+1,因为0+1=1,-1+1=0

/* 
 * logicalNeg - implement the ! operator, using all of 
 *              the legal operators except !
 *   Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int logicalNeg(int x) { //!0 = 1, !others=0
  return ((x | (~x+1)) >> 31) + 1;
}
  1. 输出最少需要的位数来表示int x
    思路:这道题太好玩了!解题思路我们分两步
    首先不考虑负数情况,当x>=0时:
    假设我们有个32位int,二进制表示为 00010100 11011111 01001010 00001100,要想看到我们最少需要多少位数,我们发现当x为正数的时候,最高位符号位为0,那么我们只需要找到从最高位往后数第一个1所处的那个位置,然后再加上1就是了。
    这里巧妙的思路就是运用二分查找!我们先看高16位是否有1,具体怎么判断呢?把x右移16位,然后取反再取反,如果是1证明最高16位有1,是0则没有。如果有1,表明我们至少需要低16位的数,把这个结果记下来。然后我们需要把x右移,那么右移多少位呢?答案是高16位如果有1,我们右移16位,如果没有1,则不右移。
    接下来继续看右移后剩下的数的最高8位!以此类推,最后把得到的结果加起来!
    那么,负数我们怎么算呢?通过观察我们发现,除了最高位符号位是1,负数需要的最高位数是从最高位往后数第一个0所处的位置,为什么呢?举个例子,比如1101,整数值为-3,也可以表示成101 (-4 + 1 = -3),所以实际上我们只需要3位就可以表示它;而对于1010,整数值为-6,则不能缩减位数。通过类似列举我们发现,可以看做从最高位往后开始找到第一个0的位置,就是对于负数需要的位数。这个时候,我们只需要把这个值给他取反~,就相当于对于正数我们从最高位开始找第一个1的过程了。
    是不是脑洞大开!
/* howManyBits - return the minimum number of bits required to represent x in
 *             two's complement
 *  Examples: howManyBits(12) = 5
 *            howManyBits(298) = 10
 *            howManyBits(-5) = 4
 *            howManyBits(0)  = 1
 *            howManyBits(-1) = 1
 *            howManyBits(0x80000000) = 32
 *  Legal ops: ! ~ & ^ | + << >>
 *  Max ops: 90
 *  Rating: 4
 */
int howManyBits(int x) {
  int f16, f8, f4, f2, f1, f0, r16, r8, r4, r2, r1, r0;

  int mask=x>>31; // x < 0, 111...1; x>=0, 000...0
  x = (mask & ~x)|(~mask & x); //if x < 0, only need to find highest 0, which is equal to use ~ then find the highest 1


  //binary search!
  f16 = !!(x >> 16); //see if high 16 has 1s
  r16 = f16 << 4; //16 or 0
  x = x >> r16;

  f8 = !!(x >> 8); //see if left high 8 has 1s
  r8 = f8 << 3; //8 or 0
  x = x >> r8;

  f4 = !!(x >> 4); //...
  r4 = f4 << 2; //4 or 0
  x = x >> r4;

  f2 = !!(x >> 2);
  r2 = f2 << 1; //2 or 0
  x = x >> r2;

  f1 = !!(x >> 1);
  r1 = f1; //1 or 0
  x = x >> r1;

  f0 = x;
  r0 = f0;

  return r16 + r8 + r4 + r2 + r1 + r0 + 1; //1 means sign bit
}

11.float x 2。
思路:可以参考书上的浮点数表达式 v = S * M * 2^E,分为M全为1的情况,全为0的情况,及一般情况。
全为1时,乘2必溢出,为NaN
全为0时,M=0.f,乘2需不需要进位决定于小数位第一位是否为1(和是否>=0.5)
其他情况为规格浮点数,只需要将E位+1就可

/* 
 * floatScale2 - 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 floatScale2(unsigned uf) {
  int s = (uf >> 31) & 1;
  int e = (uf >> 23) & 255;
  int m = uf & 0x7FFFFF;
  int a;

  if (e == 255) {//infinite
    return uf;
  }

  if (e == 0)
  {
    m = m << 1; //x2
    a = m >> 23; //to see if origin m >= 0.5, then *2 should >=1
    if (a == 1) {
      e = e + 1;
    }
    return (s << 31) | (e << 23) | m;
  }
  
  else {
    e = e + 1;
  }
  return (s << 31) | (e << 23) | m;
}
  1. float转int
    思路:这道题写起来还是需要一定的思考的,还是分3种情况
    e全为1,返回给定值
    e全为0时,返回0
    规格浮点数时,需要先把小数部分转成整数,也就是res = (1 << 23) | m,其实这个值的意思也是当E = e - Bias = 23时(单精度浮点数小数部分为23位),返回的整数值。于是可以分为当E>23时,相当于给res乘以2的E-23次方,于是我们可以用while循环来操作,这里还需要判断一下如果res溢出的情况。当E<23时,相当于乘以1/2的23-E次方,这道题可以多思考一下,会对浮点数转整数有很好的理解
/* 
 * floatFloat2Int - Return bit-level equivalent of expression (int) f
 *   for floating point argument f.
 *   Argument is passed as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point value.
 *   Anything out of range (including NaN and infinity) should return
 *   0x80000000u.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
int floatFloat2Int(unsigned uf) {
  int s = (uf >> 31) & 1;
  int e = (uf >> 23) & 255;
  int m = uf & 0x7FFFFF;

  if (e == 255) {
    return 0x80000000u;
  } else if (e == 0) {
    return 0;
  } else {
    int res = (1 << 23) | m; //res if E = 23
    int E = e - 127; //e - bias

    if (E > 23) {
      while (E > 23) {
        E--;
        res = res << 1;
        if (res < 0) {
          //E can be up to 128, so could be overflow
          return 0x80000000u;
        }
      }
    } else {
      while (E < 23) {
        E++;
        res = res >> 1;
      }
    }
    if (s == 1) {
      res = -res;
    }
    return res;
  }
}
  1. 返回2的x次方的浮点数表示
    思路,熟悉浮点数的表达公式就比较容易想,S和M都为零,只看2的x次方就好。从表达式里可以看到x=e-127,所以e=x+127,而浮点数中e需要属于0到255之间,则可得到答案
/* 
 * floatPower2 - Return bit-level equivalent of the expression 2.0^x
 *   (2.0 raised to the power x) for any 32-bit integer x.
 *
 *   The unsigned value that is returned should have the identical bit
 *   representation as the single-precision floating-point number 2.0^x.
 *   If the result is too small to be represented as a denorm, return
 *   0. If too large, return +INF.
 * 
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while 
 *   Max ops: 30 
 *   Rating: 4
 */
unsigned floatPower2(int x) {
  int e = x + 127;
  if (e < 0) {
    return 0;
  } else if (e > 255) {
    return 0x7f800000;
  } else {
    return e << 23;
  }
}

附上最终的结果:

Correctness Results Perf Results
Points  Rating  Errors  Points  Ops Puzzle
1   1   0   2   7   bitXor
1   1   0   2   1   tmin
1   1   0   2   7   isTmax
2   2   0   2   7   allOddBits
2   2   0   2   2   negate
3   3   0   2   15  isAsciiDigit
3   3   0   2   8   conditional
3   3   0   2   16  isLessOrEqual
4   4   0   2   5   logicalNeg
4   4   0   2   36  howManyBits
4   4   0   2   16  floatScale2
4   4   0   2   20  floatFloat2Int
4   4   0   2   4   floatPower2

Score = 62/62 [36/36 Corr + 26/26 Perf] (144 total operators)

lab2见!

你可能感兴趣的:(CMU 15213 深入理解计算机系统 lab 1 - data lab)