深入理解计算机系统(1)——datalab

DATALAB

内容是整数与浮点数的位级操作。

  1. bitAnd
    运用德摩根律。
/*
 * bitAnd - x&y using only ~ and |
 *   Example: bitAnd(6, 5) = 4
 *   Legal ops: ~ |
 *   Max ops: 8
 *   Rating: 1
 */
int bitAnd(int x, int y) {
      return ~(~x | ~y); }
  1. getByte
    先将x右移,用0xFF将左移后置于末尾的目标字节取出即可。
/*
 * 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;
}
  1. logicalShift
    首先要区分逻辑右移和算术右移。本题思路实际上就是将右移位全部置零。这里将1<<(y+1) 写为两个1<是为了防止n=0时的溢出。
/*
 * 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 y = 32 + (~n);//31-n
    return (x >> n) & ((1 << y) + (~0) + (1 << y));
}
  1. bitCount
    这道题目我只知道用循环的暴力做法以及运用x&(x-1),在网上又看了一些其他的做法。
    只贴代码可能一样看不懂。这里用到了五个常数0x55555555,0x33333333,0x0f0f0f0f,0x00ff00ff,0x0000ffff,5个数字的二进制写出来,分别是间隔1个0,2个0,4个0,8个0,16个0。
    计算与间隔对应的右移1,2,4,8,16次。
    是类似于分治的思想。
/*
 * bitCount - returns count of number of 1's in word
 *   Examples: bitCount(5) = 2, bitCount(7) = 3
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 40
 *   Rating: 4
 */
int bitCount(int x) {
     
  int _mask1 = (0x55)|(0x55<<8);
  int _mask2 = (0x33)|(0x33<<8);
  int _mask3 = (0x0f)|(0x0f<<8);
  int mask1 = _mask1|(_mask1<<16);
  int mask2 = _mask2|(_mask2<<16);
  int mask3 = _mask3|(_mask3<<16);
  int mask4 = (0xff)|(0xff<<16);
  int mask5 = (0xff)|(0xff<<8);

  int ans = (x & mask1) + ((x>>1) & mask1);
  ans = (ans & mask2) + ((ans>>2) & mask2);
  ans = (ans & mask3) + ((ans>>4) & mask3);
  ans = (ans & mask4) + ((ans>>8) & mask4);
  ans = (ans & mask5) + ((ans>>16) & mask5);

  return ans;
}
  1. bang
    这道题就是要将0和非零划开得到不同结果。所以要利用0的一个特殊性质就是0的相反数仍然是其本身。相反数的表示~x+1
/*
 * bang - Compute !x without using !
 *   Examples: bang(3) = 0, bang(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4
 */
int bang(int x) {
      return ((x | ~x + 1) >> 31) + 1; }
  1. tmin
    送分题
/*
 * tmin - return minimum two's complement integer
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
      return 1 << 31; }
  1. fitsBits
    将x右移n-1位,判断剩下的数字是否是全1或全0。如果符合条件,则右移后的x和x+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) {
     
    x >>= ~(~n + 1);
    return (!x | !(x + 1));
}
  1. divpwr2
    除法是靠0取整。但移位运算都是向下取整。所以当x为负数时要加入偏置值(1<((x>>31)&1< 只有当x为负数的时候才有有效值。
/*
 * 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) {
     
  return (x+(((x>>31)&1)<<n)+(~0)+(!((x>>31)&1)))>>n;
}
  1. negate
    基础知识。
/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
     
  return ~x+1;
}
  1. isPositive
    看是不是负数或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) {
      return !((x >> 31 & 1) | !x); }
  1. isLessOrEqual
    判断y-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) & 1;
    int signy = (y >> 31) & 1;
    int isSameSign = !(signx ^ signy);
    int p = !(((~x) + 1 + y) >> 31);
    int ans = (isSameSign & p) | ((!isSameSign) & signx);
    return ans;
}
  1. ilog2
    思路:找到x二进制里最高位的1。
    公式:log2(x)=16×a+8×b+4×c+2×d+1×e。
    右移16位,判是否为0,就是a的值,知道了a之后,右移(8+16×a)位,判断是否为0,得到b的值…
/*
 * 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 ans = 0;
  ans = ans + ((!!(x>>(16 + ans)))<<4);
  ans = ans + ((!!(x>>(8 + ans)))<<3);
  ans = ans + ((!!(x>>(4 + ans)))<<2);
  ans = ans + ((!!(x>>(2 + ans)))<<1);
  ans = ans + ((!!(x>>(1 + ans)))<<0);

  return ans;
}
  1. float_neg
    对符号位取反,注意特判NaN情况。
/* 
 * 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 tmp = uf&(0x7fffffff);
  unsigned result = uf^(0x80000000);
  if(tmp>0x7f800000) result=uf;
  return result; 
}
  1. float_i2f
    浮点数由符号位,阶码和小数部分组成。特别注意的是舍入部分:向偶数舍入。用(f > 128) || ((f == 128) && (tail & 1)) 来表示(f是被截掉的部分)。
/*
 * 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, whilef
 *   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;
}
  1. float_twice
    首先判断inf和NaN是返回本身。非规格化将尾数部分左移一位,规格化阶码加1,但注意变为无穷大的情况。
/*
 * 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 sign = uf >> 31;
    unsigned exp = uf >> 23 & 0xFF;
    unsigned frac = uf & 0x7FFFFF;

    if (exp == 0 && frac < 0x7FFFFF)
        frac <<= 1;
    else if (exp == 0 && frac == 0x7FFFFF) {
     
        exp += 1;
        frac = 0x7FFFFE;
    } //非规格化
    else if (exp == 0xFE) {
     
        exp = 0xFF;
        frac = 0;
    } else if (exp == 0xFF)
        return uf;
    else
        exp += 1;
    return (sign << 31) | (exp << 23) | frac;
}

运行结果:
深入理解计算机系统(1)——datalab_第1张图片
总结:
自己觉得最难的是bitCount,ilog2,float_i2f和float_twice。感觉自己只掌握了很基础的一部分知识,对于灵活使用并不熟练。浮点数的边界情况考虑,运算的溢出情况,取整的知识还有别人的巧妙思考,都是可以从中学习的。

你可能感兴趣的:(计算机系统,补码,c++)