深入理解计算机系统:datalab报告

一、实验目的

本实验目的是加强学生对位级运算的理解及熟练使用的能力。

二、报告要求

本报告要求学生把实验中实现的所有函数逐一进行分析说明,写出实现的依据,也就是推理过程,可以是一个简单的数学证明,也可以是代码分析,根据实现中你的想法不同而异。

三、函数分析

bitXor函数

函数要求:

函数名bitXor
参数int , int
功能实现x^y
要求只能使用 ~ 和

实现分析:

a^b = (a & ~b) | (~a & b), 为了将|转变为&, 对这个式子做两次取反即可。得到~(~ (a & ~b) & ~ (~a & b))

函数实现:

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

getByte函数

函数要求:

函数名getByte
参数int, int
功能实现从x中取出第n个字节
要求使用: ! ~ & ^ | + << >>

**分析:**通过x >> (n << 3)移动字节(即 n ∗ 2 3 n * 2^3 n23位),至前8位,实现目标功能

函数实现:

int getByte(int x, int n) {
	int ret;
	int t = x >> (n << 3);
	ret = t & 0xFF;
	return ret;
}

logicalShift函数

函数要求:

函数名logicalShift
参数int , int
功能实现逻辑右移
要求! ~ & ^ | + << >>

**分析:**通过变量va构建一个32位数,前n位都是0,后面的32-n位都是1,与原x进行&运算,得到结果。

函数实现:

int logicalShift(int x, int n) {
  int va = (0x1 << 31) >> n;
  x = x >> n;
  va = ~(va << 1);
  return (va & x);
}

bitCount函数

函数要求:

函数名bitCount
参数int
功能实现计算int型数据x的二进制串中1的个数
要求! ~ & ^ | + << >>

分析:

通过二分法,通过x与010101…0101, 00110011…0011,00001111…00001111…000…00111…111做&运算,依次计算 2 n ( n = 1 , 2 , 3 , 4 , 5 ) 2^n(n = 1,2,3,4,5) 2n(n=1,2,3,4,5)位中1的个数,每次计算时都利用到前一次的结果,进行加和得到最终32位数中1的个数。

首先将int型数据x的32位分成16组,并进行X31+X30,X29+X28,…,X3+X2,X1+X0的运算;然后将x分成8组,并进行X31+X30+X29+X28,…,X3+X2+X1+X0的运算。依次类推, 最后只剩下1组,此时将所有的位进行相加即得到了最终结果。

函数实现:

int bitCount(int x) {
  int mask_1 = 0x55 | (0x55 << 8);
  int mask_2=0x33|(0x33<<8);
  int mask_4=0x0f|(0x0f<<8);
  int mask_8=0xff|(0xff<<16);
  int mask_16=0xff|(0xff<<8);
  mask_1 = mask_1 | (mask_1 << 16);
  mask_2=mask_2|(mask_2<<16);
  mask_4=mask_4|(mask_4<<16);

  x = (x & mask_1) + ((x >> 1) & mask_1);
  x = (x & mask_2) + ((x >> 2) & mask_2);
  x = (x & mask_4) + ((x >> 4) & mask_4);
  x = (x & mask_8) + ((x >> 8) & mask_8);
  x = (x & mask_16) + ((x >> 16) & mask_16);

  return x;
}

conditional函数

函数要求:

函数名conditional
参数int int int
功能实现类似于C语言中的 x ? y : z
要求! ~ & ^ | + << >>

**分析:**首先通过x = !!x把x转换成1或0,再将x转换成-1和0(因为-1的补码表示全为1),通过x得到最后取得y还是取得z。

函数实现:

int conditional(int x, int y, int z) {
  x = !!x;
  x = ~x + 1;
  return ((x & y) | (~x & z));
}

tmin函数

函数要求:

函数名tmin
参数Null
功能实现返回最小的补码
要求! ~ & ^ | + << >>

**分析:**0x1左移31位得到结果

函数实现:

int tmin(void) {
	return (0x1 << 31);
}

fitsBits函数

函数要求:

函数名fitsBits
参数int int
功能实现x的补码是否可以表示成n位
要求! ~ & ^ | + << >>

**分析:**将x左移(32-n)位,再右移(32-n)位,再检查此时的数字是否与x相同,如果相同即符合条件,否则不符合。

函数实现:

int fitsBits(int x, int n) {
  int shift = ~n + 33;// 32 - n = ~n + 1 + 32
  int tmp = (x << shift) >> shift;
  tmp = tmp + ~x + 1;
  tmp = !!tmp;
  return !tmp;
}

dividePower2函数

函数要求:

函数名dividePower2
参数int int
功能实现计算x/(2^n)
要求! ~ & ^ |+ << >>

分析:

根据定义,如果是正数,直接右移n位。如果是负数,则要加上一个偏置量 2 k − 1 2^k-1 2k1

函数实现:

int dividePower2(int x, int n) {
  int sign = 0,var = 0;
  sign = x >> 31;
  var = (1 << n) + (~0);
  return (x + (sign & var)) >> n;
}

negate函数

函数要求:

函数名negate
参数int
功能实现不用负号得到
要求! ~ & ^ |+ << >>

**分析:**根据定义,补码倒数取反加一。

函数实现:

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

howManyBits函数

函数要求:

函数名howManyBits
参数int
功能实现计算表达 x 所需的最少位数
要求! ~ & ^ |+ << >>

**分析:**问题的关键是计算这个数第一个“1”出现在第几位,再加上1个符号即可得到。

找到第一个1的方法是二分查找,先将32位一分为2,计算第一个1是在前16位,还是在后16位。然后将这16位移到前16位,再查找是前8位还是后8位…最终将每次搜索的结果进行相加,加上一个符号位得到结果。

int howManyBits(int x) {
  int sign = x >> 31;// 0  -1
  int ct_16 = 0;
  int ct_8 = 0;
  int ct_4 = 0;
  int ct_2 = 0;
  int ct_1 = 0;
  int ct_0 = 0;
  int fla = 0;
  x = (sign & ~x) | (~sign & x);
  sign = ~sign + 1;

  fla = !!(x >> 16);
  ct_16 = fla << 4;
  x = x >> ct_16;

  fla = !!(x >> 8);
  ct_8 = fla << 3;
  x = x >> ct_8;

  fla = !!(x >> 4);
  ct_4 = fla << 2;
  x = x >> ct_4;

  fla = !!(x >> 2);
  ct_2 = fla << 1;
  x = x >> ct_2;

  fla = !!(x >> 1);
  ct_1 = fla;
  x = x >> ct_1;

  ct_0 = x;
  return ct_0 + ct_1 + ct_2 + ct_4 + ct_8 + ct_16 + 1;
}

isLessOrEqual函数

函数要求:

函数名isLessOrEqual
参数int int
功能实现x <= y?
要求! ~ & ^ |+ << >>

**分析:**通过判断y - x大于零还是小于零,得到结果。将y - x 转换为y + ~x + 1, 再判断溢出的特殊情况:y > 0 , x < 0 和 x > 0 , y < 0的情况。

int isLessOrEqual(int x, int y) {
  int a = y + ~x + 1;
  int sign = a >> 31;
  int s_x = x >> 31;
  int s_y = y >> 31;

  int of_1 = s_x & (!s_y);// y > 0, x < 0
  int of_2 = (!s_x) & (s_y);// x > 0, y < 0

  return of_1 | ((!of_2) & (!sign));
}

intLog2函数

函数要求:

函数名intLog2
参数int
功能实现计算⌊log2(x)⌋(向下取整)
要求! ~ & ^ |+ << >>

**分析:**与howmanybits那题相似,关键是要找到最大的1位所在的位置,取log即为二分法叠加的结果减1。

函数实现:

int intLog2(int x) {
  int ax = 1;
	int cnt_16 = 0, cnt_8 = 0, cnt_4 = 0, cnt_2 = 0, cnt_1 = 0, cnt_0 = 0;
	int flag = 0;

	flag = !!(x >> 16);
	cnt_16 = flag << 4;
	x = x >> cnt_16;

	flag = !!(x >> 8);
	cnt_8 = flag << 3;
	x = x >> cnt_8;

	flag = !!(x >> 4);
	cnt_4 = flag << 2;
	x = x >> cnt_4;

	flag = !!(x >> 2);
	cnt_2 = flag << 1;
	x = x >> cnt_2;

	flag = !!(x >> 1);
	cnt_1 = flag;
	x = x >> cnt_1;

	cnt_0 = x;
	return cnt_0 + cnt_1 + cnt_2 + cnt_4 + cnt_8 + cnt_16 + (~ax) + 1;
}

floatAbsVal函数

函数要求:

函数名floatAbsVal
参数unsigned
功能实现计算f的绝对值的位级表示
要求|| &&. also if, while

**分析:**分类讨论,除了NaN的情况,其他情况直接将s位改成0,NaN的情况(满足exp == 255且frac != 0)直接返回NaN本身。

函数实现:

unsigned floatAbsVal(unsigned uf) {
  if(((uf & 0x7F800000) >> 23) == 255 && (uf & 0x007FFFFF))
    return uf;
  else
    return uf & 0x7FFFFFFF;
}

floatScale1d2函数

函数要求:

函数名floatScale1d2
参数unsigned
功能实现计算0.5*f的位级表示
要求&&. also if, while

**分析:**先将s,exp,frac位分离出来,分类:当exp = 255返回无穷,当exp > 1的其他情况,属于规格化的数,根据IEEE浮点表示 ( − 1 ) s ∗ M ∗ 2 E (-1)^s*M*2^E (1)sM2E,只需要将exp-1即可得到结果。当exp小于等于1的时候,考虑到frac前两位的舍入,然后将原数右移一位即可。

函数实现:

unsigned floatScale1d2(unsigned uf) {
  int exp = (uf&0x7F800000) >> 23;
  int s = uf&0x80000000;
  int frac = uf & 0x7FFFFF;
  // NaN || infinity(INF)

  if(exp == 255){
    return uf;
  }else if(exp > 1){
    exp = exp - 1;
    return s | (exp << 23) | frac;
  }else if((uf & 0x3) == 0x3){
    uf = uf + 0x2;
  }
  return ((uf >> 1) & 0x007fffff) | s;
}

floatFloat2Int函数

函数要求:

函数名floatFloat2Int
参数unsigned
功能实现计算(int)f的位级表示
要求&&. also if, while

**分析:**首先提出s,exp,frac位,然后将exp-bias得到E, 分类讨论:当E小于0时,表示0附近的小数,此时直接返回0即可,当E > 31时,已经超过了int可表示的最大值,此时根据题意返回0x80000000u。由于frac最多23位,当E在0到23时,frac右移(23-E)即可得到结果,当E在23到31,frac左移(E-23)即可得到结果。

函数要求:

int floatFloat2Int(unsigned uf) {
  int s = uf & 0x80000000;
  int exp = (uf & 0x7f800000) >> 23;
  int frac = (uf & 0x007fffff) | 0x00800000;// M = f + 1
  int result = 0;
  int E = exp - 127;

  if(E < 0)
    return 0;
  else if(E > 31)
    return 0x80000000u;
  else if(E > 23){
    result = frac << (E - 23);
  }else{
    result = frac >> (23 - E);
  }
  if (s)
    return (~result) + 1;
  else
    return result;
}

四、实验总结

温故了CSAPP第二章的内容,并进行了实践。其中在CourseGrading系统中遇到了一个比较有意思的问题,就是如果在函数的中间声明变量的时候, 在./dlc bits.c的时候会报parse error的错误,修改的方式是:在编写其他语句以前,先将所有的变量进行声明。

在做实验的过程中还要考虑到各种符号的限制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Andrew_Chao

谢谢打赏喵

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值