[笔记]计算机基础 2 CSAPP Lab1-DataLab

记录一下自己所完成CSAPP的第一次Lab。
在限制了条件和循环的情况下,使用位运算进行各种操作的实现,的的确确需要对Int和Float的存储有比较深入的认知。

我所完成的Lab,一共有13个函数需要实现,满分为36分。

文章目录

  • Lab
    • 1 bitXor 位异或
    • 2 tmin 有符号整数最小值
    • 3 isTmax 判断是否是有符号整数最大值
    • 4 allOddBits 判断是否所有奇数位都为1
    • 5 negate 返回相反数
    • 6 isAsciiDigit 判断是否是Ascii的数字
    • 7 conditional 实现三元运算符x?y:z
    • 8 isLessOrEqual 判断x<=y
    • 9 logicalNeg 判断是否为0
    • 10 howManyBits 当前数所需的最小位数
    • 11 floatScale2 浮点数×2
    • 12 floatFloat2Int 浮点转整型
    • 13 floatPower2 返回2^x
  • 总结

Lab

1 bitXor 位异或

只允许使用&和~进行异或Xor的实现,结合上一张的知识,实际上就是类似于NMOS和PMOS电路的组装,如何利用真值表和高低电压保证输出。
最后使用了8/14个ops完成,具体代码如下

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

2 tmin 有符号整数最小值

理解了补码最高位的负权,就很容易知道最小值为0x80000000

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

3 isTmax 判断是否是有符号整数最大值

Tmax=0x7FFFFFFF,本体不能使用移位操作,因此必须想办法通过~运算得到较高位为1的数字。
思路上大概有两种:

  • 利用Tmax+1=Tmin,而Tmin和0都是补码==自身性质进行判断,之后排除特殊项0即可。
  • 利用移位操作,只有Tmax<<1=-2 和 -1<<1=-2,所以对-1进行排除即可,使用-1+1=0进行排除。

下列代码使用了思路2,思路1是做完后从别人那借鉴来的。

/*
 * 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) {
  // ! can transfer any number that is not zero to zero , but transfer zero to 1
  int sum=x+x;
  int pattern=(~0)^1;
  int add=x+1;
  int code1=!(sum^pattern);
  int code2=!(!add);
  int result=code1&code2;
  return result;
}

4 allOddBits 判断是否所有奇数位都为1

利用&和|操作,将符合条件的数转化为0xFFFFFFFF,之后利用!进行转化即可。

/* 
 * 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 hex_pattern=(85<<8)+85;
  int pattern1=(hex_pattern<<16)+hex_pattern;
  int pattern2=pattern1<<1;
  int code=(x&pattern2)|pattern1;
  int result=!(~code);
  return result;
}

5 negate 返回相反数

补码在实现上可以利用反码+1实现,当在理解上这很不利。

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

6 isAsciiDigit 判断是否是Ascii的数字

利用之后的isLessOrEqual思想实现即可,本质上是整数加法+符号判断

/* 
 * 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 com=(~48)+1;
  int sub=x+com;
  int code1=sub>>31;

  int com2=(~x)+1;
  int sub2=57+com2;
  int code2=sub2>>31;
  int code=(code1|code2);
  code=code+1;
  return code;
}

7 conditional 实现三元运算符x?y:z

利用!运算符,实现对于0和非0的转化,实现0和-1的编码,之后利用不同的编码对y和z进行&运算。

/* 
 * 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) {
  //x=0, a=0xFFFFFFFF
  //x!=0,a=0
  int a=(!x)<<31>>31;
  int result=(z&a)+((~a)&y);
  return result;
}

8 isLessOrEqual 判断x<=y

等价于y-x>=0,即y-x的符号位为0
在将y-x转化为y+x_com时,另外还有两个需要额外注意的点:

  1. overflow溢出,因此需要对y、x_com和sub的符号位进行检查,以确定真实结果的符号位
  2. Tmin,Tmin的补码为自身,但好在x=Tmin时,必然返回1
/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: ./
 */
int isLessOrEqual(int x, int y) {
  int y_sign=y>>31;
  int x_com=(~x)+1;
  int x_sign=x_com>>31;
  //codeMin is used for x=Tmin
  int codeMin=!(x^(1<<31));
  int sub=y+x_com;
  int sub_sign=sub>>31;
  int overflow_flag=(x_sign^y_sign)|(x_sign^(~sub_sign));
  int code1=!!(sub_sign);
  int code2=!!(overflow_flag);
  int result=(code1^code2)|codeMin;           
  return result;
}

9 logicalNeg 判断是否为0

0具有特点:0的补码还是0,且符号位为0
因此利用补码和自身的Xor,以及排除Tmin的干扰即可。

/* 
 * 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) {
  // use ~&|+<<>> to achieve !
  // only x=0 st x>=0 and x<=0
  // x>=0 & x_com>=0
  // Tmin is also same to Tmin_com, but Tmin<0
  int x_com=(~x)+1;
  int x_sign=x>>31;
  int xcom_sign=x_com>>31;
  int code=(x_sign|xcom_sign)+1;
  return code;
 }

10 howManyBits 当前数所需的最小位数

首先观察到负数x所需的位数其反码的结果一致,因此将所有负数都转化为正数进行讨论。
其次,因为操作数ops的限制没有办法,进行32次的移位以及是否为0判断,采用二分思想,分割到只剩8位时,剩余ops够用就直接ctrl c+v了
因此代码有点丑陋。

/* 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 code=x>>31;
  int pos=((~code)&x)+(code&(~x));
  int cur=1;
  //half
  int mid=1<<16;
  int mid_com=(~mid)+1;
  int sub=pos+mid_com;
  // 1<<16<=x,larger_one=0xFFFFFFFF;1<<16>x,larger_one=0
  int larger_one=~(sub>>31);
  pos=pos>>(16&larger_one);
  cur+=(16&larger_one); //16 ops
  //half of half
  mid=1<<8;
  mid_com=(~mid)+1;
  sub=pos+mid_com;
  larger_one=~(sub>>31);
  pos=pos>>(8&larger_one);
  cur+=(8&larger_one); //26 ops
  //now at least 24 bit is zero
  cur=cur+(!!pos);
  pos=pos>>1;
  cur=cur+(!!pos);
  pos=pos>>1;
  cur=cur+(!!pos);
  pos=pos>>1;
  cur=cur+(!!pos);
  pos=pos>>1;
  cur=cur+(!!pos);
  pos=pos>>1;
  cur=cur+(!!pos);
  pos=pos>>1;
  cur=cur+(!!pos);
  pos=pos>>1;
  cur=cur+(!!pos);
  return cur;
}

11 floatScale2 浮点数×2

可以使用if后,只需要进行分情况讨论即可。

  1. 对于无穷和NaN,返回uf即可。
  2. 对于阶码为0,直接相加即可。
  3. 对于阶码规范,阶码+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 a rgument
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4
 */
 //finish
unsigned floatScale2(unsigned uf) {
  unsigned a=uf>>23;
  unsigned b=a&255;
  unsigned c=(b<<8)+b;
  unsigned code=(c<<16)+c;
  unsigned m=uf<<9>>9;
  unsigned result=m+uf;
  unsigned add=1<<23;
  unsigned result2=uf+add;
  // E is 11111111
  if(!(~code))
    return uf;
  // E is 00000000
  if(!code)
    return result;
  // 00000001<=E<=11111110
  return result2;
}

12 floatFloat2Int 浮点转整型

只能说还好舍入规则简单,而且可以使用大于>和小于<符号,不然一直补码+移位+!+!的操作进行逻辑判断,ops完全不够用。
一样针对,<0、>=2^31、<- 2^31的情况进行特殊处理,之后利用M* 2 ^ 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) {
  //E<=125 -> int=0
  //unsigned is 0 fill
  int S=(uf>>31);
  int E=(uf<<1)>>24;
  unsigned M=((uf<<9)>>9)+(1<<24);//6 ops
  int Bias=127;
  int Bias_com=(~Bias)+1;
  int iE=E+Bias_com;
  int minus24=(~24)+1;
  int Diff=iE+minus24;
  int Diff_com=(~Diff)+1;
  int result1=(M<<Diff);
  int result2=(M>>Diff_com);
  int result3=(~result1)+1;
  int result4=(~result2)+1;

  //too small
  if(iE<0)return 0;
  //too larger
  if(iE>30)return 1<<31;
  // <0
  if(S>0)
  {
    if(Diff>0)return result3;
    return result4;
  }
  if(Diff>0)return result1;
  return result2;
}

13 floatPower2 返回2^x

使用浮点数返回2的次方,进行分情况讨论即可。

/* 
 * 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) {
    //x>=128  7
    int a=128;
    int a_com=(~a)+1;
    int sub1=a_com+x;
    int code1=sub1>>31;
    unsigned inf=255<<23;
    unsigned zero=0;
    unsigned normal=(x+127)<<23;
    unsigned non_normal=1<<(x+149);
    int a1=150;
    int a_com1=(~a1)+1;
    int x_com=(~x)+1;
    int sub2=x_com+a_com1;
    int code2=sub2>>31;
    int a2=127;
    int a_com2=(~a2)+1;
    int sub3=a_com2+x_com;
    int code3=sub3>>31;
    if(!code1)
      return inf;
    //x<=-150  7
    if(!code2)
      return zero;
    //-149<=x<=-127  6
    if(!code3)
      return non_normal;
    //126<=x<=127  2
    return normal;
}

总结

不得不承认,这些实验的设计着实让我费尽心思地去思考Int和Float的存储格式,受益匪浅。
第一次提交的时候来了个20/36的分数,其中logicalNeg和floatFloat2Int两道4分题,直接理解错题意,真的好笑。
Lab算是写了1天,然后Debug花了额外1天,算是花了两天才写完吧,有点费劲的。
——2023.3.17

你可能感兴趣的:(ICS,CSAPP,笔记,c++,算法,c语言)