没时间看CSAPP?那总要看看实验吧——DATALAB | 牛气冲天新年正文

383 阅读1分钟

写本文章的初衷,一个是本着分享的精神,在做实验的过程中记录下自己哇哦哇哦的知识点,另一个也是一种对自己的督促,毕竟自己做总是没什么动力,来过看过觉得不错的,评论个催更,也算是给我个学习动力。

0. 本节内容

在这次实验中,我们在实现功能的同时,而摒弃使用for,if等控制结构(部分),目的是深刻掌握位运算,打下坚实的基础

1. bitXor

实验描述

/* 
 * bitXor - x^y using only ~ and & 
 *   Example: bitXor(4, 5) = 1
 *   Legal ops: ~ &
 *   Max ops: 14
 *   Rating: 1
 */
 int bitXor(int x, int y) {}

通过 ~,&两个操作符实现异或功能

思路

x\y01
001
110

本题难点其实在于操作符号的限制上,由上表易得

A ^ B = (A & ~B) | (~A & B)

但这里不能使用|因此不妨利用AB=AB\overline {A \bigcup B} =\overline{A} \bigcap \overline B 两次取反有

int bitXor(int x, int y) {
    return ~((~(~x&y))&(~(x&~y)));
}

2. tmin

实验描述

/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */

返回INT_MIN

思路

直接使用移位操作符

int tmin(void) {

  return 1 << 31;

}

3. isTmax

实验要求

/*
 * isTmax - returns 1 if x is the maximum, two's complement number,
 *     and 0 otherwise 
 *   Legal ops: ! ~ & ^ | +
 *   Max ops: 10
 *   Rating: 1
 */

判断是否参数x是否为int最大值

思路

INTMAX在硬件中表示为除符号位均为1,以四位为例,我们可以发现0111+1 = 1000,这也是0111的反码,在提交后我们发现 -1,即1111也具备该特征(由于溢出),因此我们要考虑去除-1,即x+1 == ~x && x != -1时,return 1 否则返回0。

但实验要求我们不能使用这些操作符,因此我们需要使用 ! ~ & ^ | +分别表示==,&&,!=

这里可以使用之前学到的,异或,来表示是否等于, 由于返回值只有1和0,因此可以用位运算$代替‘且’,而x != -1也就是x + 1 != 0——> !(x + 1 == 0)——> !(x + 1),有

int isTmax(int x) {
  return !((x + 1)^(~x)) & !!(x+1);
}

4. allOddBits

实验要求

/* 
 * 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
 */

如果所有奇数位为1,则return 1 ,否则返回0 注意:从0开始计数

思路

判断奇数位是否均为1最简单的方法是将x与0xAAAAAAAA异或运算判断是否相等,这个之前已经讲过了

int allOddBits(int x) {
  int t = 0xAAAAAAAA;
  return !((t & x) ^ t);
}

5. negate

实验要求

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */

返回相反数

思路

这个学过计组的都应该知道,取反加1,不知道的推荐看下CSAPP,国内很多教材又是补码又是反码的其实讲的很乱,

int negate(int x) {
  return ~x + 1;
}

6. isAsciiDigit

实验要求

/* 
 * 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
 */

如果x的硬件表示0x30 <= x <= 0x39,则返回1,否则返回0

思路

分步做,先看高4位是否为3,判断相等用异或,再看低4位是否小于等于9,我们通过为0x9取反,并加上低4位,如果低4位大于9,此时应该会发生溢出。【 这部分知识点要听了CSAPP才好想出来,因为课中讲了为什么设计取反加一来表示负数

int isAsciiDigit(int x) {
  int pre = x >> 4 ^ 0x3;
  int d = x & 0xF;
  int c = ~0x9+d;
  int b = 0x80000000;

  return !pre & !((c & b) ^ b);
}

7. conditional

实验要求

/* 
 * conditional - same as x ? y : z 
 *   Example: conditional(2,4,5) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */

如果x 为 true【非0】 return y 否则 return z

思路

看起来就是x && y || !x && z,然而操作码要求替代||,&& ,注意,之前使用&直接代替&&是因为数字只有一位,现在需要用和yz等长的0或1进行位运算了

int conditional(int x, int y, int z) {
    
  int a = 0xffffffff;
  
  return (~(a + !!x) & y) + ((a+!!x) & z) ;
}

8. isLessOrEqual

实验要求

/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */

如果 x <= y return 1, 否则 return 0

思路

这道题很容易想到 x - y <= 0,或者发现≤不会表示,机智的想到了y - x > 0,而这道题的坑其实是要考虑溢出问题,那么不妨考虑减法什么时候会发生溢出,即只有负数减一个正数的时候,正数减负数时才会出现溢出。其中但此时又可以明显看出两者大小关系,因此我们使用符号位来代替溢出时的状况 同时,我们可以通过移位操作来判断符号位

int isLessOrEqual(int x, int y) {
    int a = x >> 31;
    int b = y >> 31;
    // x + y  -
    int c = a& ~b;
    int d = ~a& b;
    
  return  (c & 1) |(~c & ~d & !(!!((~x+1+y) >> 31)));
}

9. logicalNeg

实验要求

/* 
 * 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 
 */

参数为0则return 1否则return 0

思路

  • 正数的相反数是负数,负数的相反数是正数,只有0的相反数是自己
  • 正数和0的操作码都是0,但0的操作码等于自己
多个思路,自己写个

10. howManyBits

实验要求

/* 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
 */

本题问参数x可由几位表示,需要注意的是: 需要符号位, 负数前几个都是1的位数不算在内

思路

!!!!!

这道题一般我们都是用for循环来处理,但这里不允许,甚至不可以用if,所以使用了一个神乎奇技的二分查找 [二分查找其实没什么难的,但每次出现我都妙啊妙啊]

int howManyBits(int x) {
    int flag = x >> 31 ;
    int t = (~flag & x) | (flag & ~x );
    // er fen 
    int b16 = !!(t >> 16) << 4;
    t = t >> b16;
    int b8 = !!(t >> 8) << 3;
    t = t >> b8;
    int b4 = !!(t >> 4) << 2;
    t = t >> b4;
    int b2 = !!(t >> 2) << 1;
    t = t >> b2;
    int b = !!(t >> 1);
    t = t >> b; 
    return b16 + b8 + b4 + b2 + b + t + 1;//+sign
}

11. floatScale2

实验要求

/* 
 * 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
 */

return 2 ×\times f

思路

做这题之前需要补充下浮点数知识点

1.IEEE754

(1)SM2E(-1)^S*M*2^E

规格SEM
单精度1823
双精度11152
临时浮点数11564

2.规格化

  • 阶码(exp)不全为0或全为1

  • E=Exp-Bias

    • Bias:2k11其中k为阶码位数2^{k-1}-1 其中k为阶码位数

    • 通过这种方式保证Exp为无符号数,方便比较大小

    • 0Exp255127E1280 \leq Exp \leq 255 \\-127 \leq E \leq 128
  • M:1.xxxx 隐藏1

    • Min: frac=000 (M=1.0)
    • Max:frac=111(M=2.0-ϵ\epsilon)
  • 特殊值

expfrac
111000\infty
111非000NaN(1,,0\sqrt{-1},\infty-\infty,\infty *0)
000当作小数处理非规格化:E=1-bias

综上,除了简单的exp+1的场景外,考虑几个特殊值即可

unsigned floatScale2(unsigned uf) {
  int exp = (uf & 0x7f800000) >> 23;
  int sign = uf >> 31 &0x1;
  int frac = uf & 0x7FFFFF;
  
  // exp 111
  if (! (exp ^ 0xFF))return uf; 
  // exp 000
  if(! (exp ^ 0)){
      frac <<= 1;
      return sign << 31| exp << 23 | frac;
  }
  return sign << 31| (exp + 1)<< 23 | frac;
  
}

12. floatFloat2Int

实验要求

/* 
 * 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
 */

将float转变为int

思路

这里通过类似验码加移位的操作方式得到Expfracsign的值,其中sign由于在第一位,右移会时符号位一起移动,所以再次用验码相交。

这次主要考虑如下几点

  • e在0 到 31之间,正常计算,其中如果e < 23,无法将所有的frac化为正数,要舍去23-e位,否则将frac左移e-23位
  • e小于0,则返回0,e大于31或出现其他特殊值,则返回 0x80000000u.
  • 考虑sign
int floatFloat2Int(unsigned uf) {
    int exp = (uf & 0x7f800000) >> 23; //fuhao
    int sign = uf >> 31 &0x1;
    int frac = uf & 0x7FFFFF;
    int e = exp - 127;
    
    if(e < 0){
        return 0;
    }
    else if(exp == 0xFF|| e >= 31) return 0x80000000u;
    frac = 1<<23 | frac;
    if(e < 23){
        frac = frac >> 23-e;
    }else{
        frac = frac << e-23;
    }
    
    if(sign == 0){
        
        return frac ;
    }return -frac;
    
}

13. floatPower2

实验要求

/* 
 * 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
 */

返回2x2^x

思路 同样需要对x的范围进行讨论,如前文所述,exp取值范围位127 , -126,而如果x小于-126,则需要用frac来代替,但最少为-148

unsigned floatPower2(int x) {
//     if(x < -126){return 0;
    int t;
    if(x < -148){
        t = 0;
    }else if(x > 127){
        t =  0xFF << 23;
    }else if(x <= 127 && x >= -126){
        t =  x + 127 << 23;
    }else{
        t =  1 << x + 148;
    }
    return t;
}