CSAPP-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);
}

2.getByte

提取第n个Byte,即 【2n-1 -1,2n -1】 位。把【0,2n-1 -1】去掉,然后取低8位

/*
 * 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;//get low 8 bit
  return tmp;
}

3.logicalShift

逻辑右移,高位补0。作算数右移,然后把负数的high bit置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) {
  return (x>>n) & (~( (0x1<<31)>>n<<1 ));
}

4.bitCount

计算 int1 的个数,可以将 32 位相加,最终的和就是 1 的个数。

参考:https://stackoverflow.com/questions/3815165/how-to-implement-bitcount-using-only-bitwise-operators

由于操作符限制为 40 个,需要尽可能在一次操作内完成多位相加,采用分治归并的思想可以解决问题(类似归并排序):

  • 首先将 32 位分为 16 组,每组为连续 2 位,令每组前后 1 位相加,得到的值存储在该 2 位中
  • 再将 32 位分为 8 组,每组为连续 4 位,将 4 位的前后 2 位相加,得到的值存储在该 4 位中
  • 依次类推,最终得到 32 位相加的值
  • n 位长度的数只需要相加 logn 次即可
B[0] = 0x55555555 = 01010101 01010101 01010101 01010101
B[1] = 0x33333333 = 00110011 00110011 00110011 00110011
B[2] = 0x0F0F0F0F = 00001111 00001111 00001111 00001111
B[3] = 0x00FF00FF = 00000000 11111111 00000000 11111111
B[4] = 0x0000FFFF = 00000000 00000000 11111111 11111111
/*
 * 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) {
  unsigned int c; // store the total here

  c = (x & 0x55555555) + ((x >> 1) & 0x55555555);
  c = (c & 0x33333333) + ((c >> 2) & 0x33333333);
  c = (c & 0x0F0F0F0F) + ((c >> 4) & 0x0F0F0F0F);
  c = (c & 0x00FF00FF) + ((c >> 8) & 0x00FF00FF);
  c = (c & 0x0000FFFF) + ((c >> 16)& 0x0000FFFF);
  return c;
}

5.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) {
  // 0 | (~0+1) is 0x00000000, but others are 0xffffffff
  return ((x | ~x + 1) >> 31) + 1;
}

6.tmin

1000…000

/*
 * tmin - return minimum two's complement integer
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
  return 1<<31;
}

7.fitsBits

判断 x 是否可以被 n 位的二进制补码所表示

如若一个数能够被n位二进制数表示,则其第n位即最高位是符号位,那么将其右移n-1位后,根据算术右移,其得到的结果不是0,就是1。否则表示,其还有高于n位的位数, 即不能用n位表示。

/* 
 * 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)
  int k = x >> (n + ~0); // if can, k = 0 or -1
  return !k | !(k + 1);
}

8.divpwr2

求x/2n ,要求向0取整。 x >> n即为,结果是向下取整的,若x非负向下取整便是向0取整没有问题,若x为负,须要向x加上一个偏移值(2^n - 1),使得x >> 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 signx = x >> 31;//全0或者全1
    int mask = (1 << n) + (~0);//2^n -1
    int bias = signx & mask;//如果x是正数,则bias为0;
    //如果x为负数,bias=2^n -1
    return (x + bias) >> n;
}

9.negate

/*
 * negate - return -x
 *   求相反数
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
  return ~x+1;
}

10.isPositive

看是不是正数,注意要区分0,| (!x)

/* 
 * 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) | (!x)); //看符号位
}

11.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 isSameSign = !( (x^y)>>31 );
  int sub_sign = !(( (~x) + 1 + y) >> 31);
  int ans = (isSameSign & sub_sign) | ((!isSameSign) & signx);
  return ans;
}

12.ilog2

采取二分法:每次将查找区间分为两份,首先使用两次取非来判断高位是否存在 1,若有则在高位区间继续二分查找,若没有则在低位区间继续二分查找,直到区间长度为 1

/*
 * ilog2 - return floor(log base 2 of x), where x > 0
 * 采取二分法:每次将查找区间分为两份,首先使用两次取非来判断高位是否存在 1,
 * 若有则在高位区间继续二分查找,若没有则在低位区间继续二分查找,
 * 直到区间长度为 1。
 *   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;
}

13.float_neg

对一个浮点数求负数,在值为 NaN 的情况下返回原值。

exp=0xff, frac!=000…000,时,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) {
    int isNaN = (((uf >> 23) & 0xFF) == 0xFF) && (uf << 9);
    return isNaN ? uf : ((1 << 31) ^ uf);
}

14.float_i2f

intx 转换为 float 型,按照二进制位编码输出。注意,float(x)是向偶数取整的。

float 型编码可以分解为 s-e-f 格式。符号位,阶码,尾数,舍入,一个一个来。

首先取出符号位sign

之后要得到除符号位以外,最高位的 1 所在的位数,即指数E,exp = E+127

最高位的 1 之后的 23 位即是尾数frac。

如果最高位的 1 之后超过 23 位,则需要进行向偶舍入。将其取出并在低位补足 8 位,判断是否大于 0.5,如果相等则判断尾数末位是否为 1,若是则进位。进位时若尾数部分溢出则阶码需要 +1

/* 
 * 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) {
    if(!x) return 0;//x=0
    
    unsigned sign = x & (1 << 31);
    unsigned exp = 0;
    unsigned frac = 0;
    unsigned round = 0;

    unsigned absX = sign ? (~x + 1) : x;
    unsigned tmp = absX;
    while ((tmp = tmp >> 1))
        ++exp;

    frac = absX << (32 - exp);//去掉frac前面的0
    round = frac << 23 >> 23;
    frac = frac >> 9;//截断,保留23位

    if (round > 0x100) round = 1;
    else if (round < 0x100) round = 0;
    else round = frac & 1;//round to even
	
    //或运算:舍入如果溢出,直接exp+1,nice
    return  (sign | ((exp + 0x7F) << 23) | frac) + round;
}

15.float_twice

对使用二进制位表示的浮点数f,返回 2 f的二进制位表示,如果为 NaN 则返回原值。

隐含条件:如果值为 INF 也返回原值,所以不需要判断是否为 INF

  • 若全 1 则返回原值
  • 若全 0 则令其尾数左移 1 (若溢出则阶码 +1 )
  • 若既有 0 又有 1 则阶码 +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 = 1 << 31;
    unsigned isNormalized = uf << 1 >> 24;
    unsigned isSpecial = (isNormalized == 0xFF);

    //special and zero : 2x = x 
    if (isSpecial || uf == 0 || uf == sign)
        return uf;
    //normalized number: exp+1
    if (isNormalized)
        return uf + (1 << 23);
    // denormalized: whether overflow
    return (uf << 1) | (uf & sign);// restore the sign for negtive float
}

总结

觉得最难的是bitCount,fitsBits, ilog2,float_i2f和float_twice。感觉自己只掌握了很基础的一部分知识,对于灵活使用并不熟练。浮点数的边界情况考虑,运算的溢出情况,取整的知识还有别人的巧妙思考,都是可以从中学习的。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值