【深入理解计算机系统】CSAPP-实验一:DataLab思路以及代码

前言

实验说明

datalab主要是对整型以及浮点型的实验,其中对条件语句、算数运算以及逻辑运算限定了不同的规则。

本机使用win10 +wsl2.0 + ubuntu18.04完成实验。

得分满以及dlc检验可过。

点击打开我的github,查看我的全部代码

Reference

遇到问题参考了以下博主的文章:

DataLab思路+答案

datalab我最后一题总是pass不了,感觉应该是机器环境的问题

bitXor

* bitXor - x^y using only ~ and &

* Example: bitXor(4, 5) = 1

* Legal ops: ~ &

* Max ops: 14

* Rating: 1

思路

逐渐展开。

x^y = ( ~x&y | x&~y )

然后根据 (a|b) = (a&~b)把上面的或写成与

代码

int bitXor(int x, int y) {

  int lhs = ~x & y;
  int rhs = x & ~y;

  return ~( ~lhs & ~rhs );
}

tmin

* tmin - return minimum two’s complement integer

* Legal ops: ! ~ & ^ | + << >>

* Max ops: 4

* Rating: 1

返回最小的int值

代码

int tmin(void) {
  return 1<<31;
}

isTmax

* 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) {

  int Tmax = ~(1<<31);

  return !(x^Tmax);
}

然后题目不允许。只能通过别的方式。

可以知道: Tmax + 1 = 0x8fffffff,而 2 * 0x8fffffff = 0.
根据这个性质可以判断。是否有反例?有一个:2 * 0 = 0,因此要排除掉这个。

代码

int isTmax(int x) {

  int x_plus1 = x+1; //x_plus1得排除是0的情况(即x=0xffffffff)
  int x_plus1_double =x_plus1+x_plus1;

  return !x_plus1_double & ( !!x_plus1 );
}

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

思路

生成一个mask,把输入x的所有奇数位置的值取下来(与)。

然后再比对两个mask与该值是否一致。

代码

int allOddBits(int x) {
  int i = 1<<1;
  i = (i<<2) | i;
  i = (i<<4) | i;
  i = (i<<8) | i;
  i = (i<<16) | i; //mask

  return !((x&i)^i);
}

negate

* negate - return -x

* Example: negate(1) = -1.

* Legal ops: ! ~ & ^ | + << >>

* Max ops: 5

* Rating: 2

代码

int negate(int x) {

  return ~x + 1;
}

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

思路

用于Bit的范围判断。只能不断列举。

0x30是0011,0000. 而0x39是0011,1001.

找出这个范围内的Bit规律

  • 首先第五第六位Bit必须都是1
  • 如果第四位bit是1,则第一位是0或1都可以,但2和3位要是0
  • 如果第四位bit是0,第一第二第三怎样都可以。

代码

int isAsciiDigit(int x) {

    int mask = ~(0xf);
    int bit5_6 = x & mask;
    int bit4 = (x>>3) & 1;
    int bit3 = (x>>2) & 1;
    int bit2 = (x>>1) & 1;
    return !(bit5_6^(3<<4)) & ( !bit4|bit4&( !bit2 & !bit3 ) );
}

conditional

* conditional - same as x ? y : z

* Example: conditional(2,4,5) = 4

* Legal ops: ! ~ & ^ | + << >>

* Max ops: 16

* Rating: 3

思路

如何找一个合适的mask作为控制是最难的地方。

从0xffffffff入手是不错的思路

mask = 0xffffffff+!x。

代码

int conditional(int x, int y, int z) {

  int mask = ~0 + !x;
  return (mask&y ) | (~mask & z);
}

isLessOrEqual

* isLessOrEqual - if x <= y then return 1, else return 0

* Example: isLessOrEqual(4,5) = 1.

* Legal ops: ! ~ & ^ | + << >>

* Max ops: 24

* Rating: 3

思路

  • LESS:相减,判断符号
  • Equal:异或判断是否相等

以为这就结束了吗?

还得判断是否会溢出。

考虑溢出情况:

  • 正数 - 负数
  • 负数 - 正数

因此,如果是前者,就直接返回true,如果是后者,就直接返回false。

但是bit的写法会比较特殊。看看代码

代码

int isLessOrEqual(int x, int y) {
  int sum = x + ~y +1;
  return 
  (!(x^y) // xy相等
  |( sum>>31)&1 //x+y为负数
  | ((x>>31)&1) & !((y>>31)&1)) //x为负数、y为正数
  & ! (!((x>>31)&1) & ((y>>31)&1)) ; //这里必须是and
}

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和1.

映射关系为:

  • 如果x=0,则输出1
  • 如果x=其他,则输出0

因此我们要找出0以及其他的最大不同点,以完成投影。
那便是 +0 = -0 = 0。即 ~x +1 的符号位 = x的符号位。

代码

int logicalNeg(int x) {
  return   ((~(~x + 1) & ~x )>>31 & 1); //这里之所以要给0和+0都取反,再与,是为了返回需要的1。如果不取反,就会返回成0
}

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

思路

我们把例子都写一下,就知道这个结果是怎么来的。

  • 12: 0.1100
  • 298: 01,0010,1010
  • -5: 1101
  • 0: 0
  • -1: 1

所以:正数的res = 1的最高位位置m +1

负数的res = 取反后:1的最高位位置m +1

现在问题变成,如何找出最高位

可以人工分治。

代码

copy version

int howManyBits(int x) {
 int b16,b8,b4,b2,b1,b0;
	int sign=x>>31;
	x=sign&(~x) | (~sign)&x;//如果x为正则不变,否则按位取反(统一找最高位为1)
	b16=!!(x>>16)<<4;//判断前16位是否有1,并且同时计算了需要移位的位数
	x=x>>b16;//有1就移动16位,否则b16=0,不移动
	b8=!!(x>>8)<<3;
	x=x>>b8;
	b4=!!(x>>4)<<2;
	x=x>>b4;
	b2=!!(x>>2)<<1;
	x=x>>b2;
	b1=!!(x>>1);
	x=x>>b1;
	b0=x;
  return b16+b8+b4+b2+b1+b0+1;
}

floatScale2

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

思路

这题很好地考察了规格化数与非规格化数。

以及自己琢磨一下这个例子,这题就会了

代码

unsigned floatScale2(unsigned uf) {
  unsigned s = uf & 0x80000000;
  unsigned exp = uf & 0x7f800000;
  unsigned frac = uf & 0x007fffff;

  if(!exp){ // 非规格化数
    frac<<=1;
  }
  else if(exp^ 0x7f800000){ //规格化数(非规格化数除去NAN)
      exp+=0x00800000;
      //相加之后可能变成无穷大,此时要将尾数置0
      if(!(exp^0x7f800000)){
        frac = 0;
      }
  }

  return s|exp|frac;
}

floatFloat2Int

* 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

思路

这里关键是理解unsigned表示的flaot以及转成int的过程的移位区别。

如果uf什么都不动,变成int的时候就已经是默认小数点右移动(变大)23了。

代码

int floatFloat2Int(unsigned uf) { //吊

  int tmin=0x1<<31;//溢出需返回的值
	int sign=uf>>31;//取符号
	int frac=uf&0x007fffff;//取尾数
	int E=((uf>>23)&0xff)-127;//取指数  Exp-偏置( 2^7 -1)=127
	if(E<0) return 0; //如果指数小于0,相当于尾数小数点左移,必定为小数,舍入为0
  int M;
 	M = frac|(0x1<<23);//能到这里的都是规格化数,补充隐藏位1.
 

	if(E>31) return tmin;//如果指数大于31,相当于尾数小数点右移31,溢出
	if(E>23) M=M<<(E-23);//如果指数>23,小数点左移E-23
	else M=M>>(23-E);	//否则右移23-e

	if(!( (M>>31) ^sign)) return M;//如果移动完成的尾数M符号位与sign相同,无需转换,直接返回M
	else if(M>>31) return tmin;//如果不同,且M为负数,则无法表示(需更多位),返回溢出
	else return ~M+1;	//如果不同,且M为正数,返回其相反数
}

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

思路

这题很简单。

只需要根据 E = e - bias反推e就好了。

代码

unsigned floatPower2(int x) {

	int inf=0x7f800000;
	if(x<-126) return 0;
	if(x>127) return inf;
  int e = x+127;
	return e<<23;
}

后话

刚入学大四,也准备保研本校。
制定一些持续学习目标,有目的地完成。《深入理解计算机系统》的实验是我的计划之一。

具体目标:

  • 完成所有感兴趣的实验
  • 有计划地完成所有感兴趣练习题
  • 有选择的看视频课程

欢迎留言与我交流。

  • 5
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值