CSAPP datalab 学习全程

 写这个之前最好先把《深入理解计算机系统》的第二章学一下,或者有一点点计算机基础的也可以跟我一样硬写(但还是有一点痛苦的),然后写到最后三题的时候学一下IEEE754浮点数格式。

由于我用的是Mac,得搭一下环境,详看这篇文章(window的环境怎么搭也在里边,不搭环境但是初次接触datalab也可以看一下,会对datalab有更清晰的认识):

Introduction to CSAPP(八):Datalab - 知乎

 写的时候注意符号优先级,不确定就还是按想要的顺序加上括号吧,还要注意规范化。

看不懂我的解析可以去看一下这篇:

CSAPP:DataLab详细解析_prician的博客-CSDN博客_datalab

这篇文章对我的帮助很大。

1.bitXor(x, y)

要求:仅使用 ~ 和 & 完成异或运算。

运算:~ &

做法:画个真值表

进行一个德摩根律的应用

 按照德摩根律写出代码,即:

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

2. tmin()

要求:返回二进制补码表示的最小整数。

运算: ! ~ & ^ | + << >>

做法:返回1000000...(31个0)此为补码表示的最小整数,即只需将1左移31位即可

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

3.isTmax(x)

要求:判断 x 是否为补码表示的最大整数,是则返回1。

运算:! ~ & ^ | +

做法

在32位中,补码表示的最大整数为011111...,即该题目为判断x是否为0111111...,若是,则满足:(~x)^(x+1)=0(在纸上写出来看看),那么此时返回(!(~x^(x+1)))即可返回1;

但是当x为11111...时,也满足(~x)^(x+1)=0,那么我们需要排除这个情况,当x为1111...时,(~x)^0x0=0,将其结果规格化,得到(!!(x+1)^0x0),这一步是使x=11111...时返回0,使x为011111...时返回1。

将以上两个式子进行&运算即可。

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

4.allOddBits(x)

要求:判断 x 二进制表示下的奇数位是否全为 1,是则返回1。

运算:! ~ & ^ | + << >> (规则允许使用最大为 0xFF 的整数

做法

只需判断奇数位是否全为1即可。(注意二进制数是从第0位开始的)

一个随便的8位二进制数,若其奇数位全为1,&10101010后,得到10101010,得到的结果^10101010后为0,那么一个随便的32位二进制数同理,所以我们首先需要获得10101010...(32位)这个数。

10101010为0xAA,我们将其左移8位后再 | 0xAA,得到0xAAAA:10101010...(16位),

同理也可得到10101010...(32位),再按照上面的逻辑编写代码即可。

int allOddBits(int x)
{
  int a = 0xAA;
  int aa = a | (a << 8);
  int aaa = aa | (aa << 16);

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

5.negate(x)

要求:计算返回 -x。

运算:! ~ & ^ | + << >> 

做法:-x=~x+1,直接返回就好了

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

6.isAsciiDigit(x)

要求:判断值 x 是否在范围 [0x30, 0x39] 中。

运算: ! ~ & ^ | + << >>

做法

若x在这个范围内,则 0x39+(-x)>=0,x+(-0x30)>=0;

由上题可知,-x=~x+1,即可得到下列式子,将式子的结果分别赋值给a,b,再获得他们各自的符号位即可判断x的值是否在范围内了。(正数符号位为0,负数符号位为1)

int isAsciiDigit(int x) {

  int a = 0x39+(~x+1);
  int b = x+(~0x30+1);
  int c = !(a>>31) & !(b>>31);

  return c;
}

7.conditional(x, y, z)

要求:执行 x ? y : z:当 x 不为 0 时,返回 y;否则返回 z。

运算:! ~ & ^ | + << >>

做法

当x不为0时,!!x=1,将其变为其相反数-1(为111111...)后&y,即可返回y;

当x为0时,!!x=0,其相反数仍为0,则将其相反数取反后得到(11111....)&z后,即可返回z。

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

8.isLessOrEqual(x, y)

要求:判断 x <= y,x<=y时返回1。

运算:! ~ & ^ | + << >>

做法

判断y-x>0,可以将y+(~x+1)的结果赋值给a,通过判断a的符号位,来判断y-x是否>0。

在x=y时也需返回1,此时我们返回!(x^y)即可。

但那么做具有局限性:

1.在a的符号位为0时,此时应该为y-x>0,返回1,

但当y<0,x>0时,算出:y-x>0,为错的,因此这种情况应返回0。

2.在a的符号位为1时,此时应该为y-x<0,返回0,

但在y>0,x<0时,算出:y-x<0,为错的,因此这种情况应返回1。

因此我们不只需要获取a的符号位,还要获取x,y的符号位进行判断。

int isLessOrEqual(int x, int y) {
 int a = y + (~x+1);
 int f1 = !(x^y);
 int fa = a>>31;
 int fx = x>>31;
 int fy = y>>31;
 return ((!fa)&((!fy)|fx)) | ((!fy)&fx&fa) | f1;
}

9.logicalNeg(x)

要求:计算 !x:当 x = 0 时返回 1;当 x ≠ 0 时返回 0。

运算:~ & ^ | + << >>

做法:当x不等于0时,x|(-x)的符号位为1,此时取反

int logicalNeg(int x) {
  int a = (x | (~x+1))>>31;
  return (~a)&1;
}

 10.howManyBits(x)

要求:使用二进制补码表示 x 的最少位数。

运算:! ~ & ^ | + << >>

做法

当x>=0时,位数取决于1的最高位数;当x<=0时,位数则取决于0的最高位数。那么我们先将负数取反,将问题统一成计算 1 的最高位,算术右移获得符号位后再与自身进行异或即可完成。

使用二分查找法去找1的最高位数,先将32位数分为16位,再分为8位,4位,2位,1位进行查找。

拿出一个细说

res为计数器,首先一定有符号位,所以计数器初始值为1。

在第一个二分中,我们先将x分为前16位和后16位,规格化前16位(!!(x>>16))可以知道前16位有没有1,如果有,则x的位数肯定包括后16位,此时!!(x>>16)=1,计数器加上1*16(左移4位即为乘以16)可以获得目前已知的位数;再将x右移16位,即可将前面的16位再继续二分;如果前16位没有1,则将后面的16位继续二分查找,直到得到x的最少位数。

 bit = !!(x>>16)<<4;
  res = res + bit;
  x = x >> bit;

 详细代码如下:

int howManyBits(int x) {
  int bit;
  int res = 1;
  x = x ^ (x >> 31);

  bit = !!(x>>16)<<4;
  res = res + bit;
  x = x >> bit;

  bit = !!(x>>8)<<3;
  res = res + bit;
  x = x >> bit;

  bit = !!(x>>8)<<3;
  res = res + bit;
  x = x >> bit;

  bit = !!(x>>4)<<2;
  res = res + bit;
  x = x >> bit;

  bit = !!(x>>2)<<1;
  res = res + bit;
  x = x >> bit;

  bit = !!(x>>1);
  res = res + bit;
  x = x >> bit;

 return x + res;
}

11.floatScale2(uf)

要求:计算 2 * uf,若 uf 为特殊值值时,直接返回 uf。

运算:Integer/unsigned 相关运算;||,&&,if 和 while 等判断语句。

做法

去找一下IEEE754浮点表示看一下,再回来会一目了然。

如果uf为特殊值,即e=0xff,直接返回uf;

如果uf为非规格化数,即e为0,乘上系数 2 时,阶码是否变动看 2f 是否大于等于 1,即 f 最高位是否为 1。由于阶码在尾数的高位,该情况下位数左移 1 位即可。

如果是规格化数,e+1后,将s,e,f分别进行移位运算再用|连在一起就好了。

详细代码如下: 

unsigned floatScale2(unsigned uf) {
  unsigned s = uf >> 31 & 0x1; //取得s

  unsigned e = uf >> 23 & 0xff; //&上111111111,取得exp

  unsigned f = uf ^ (s << 31) ^ (e << 23); //取得frac
  //也可以 unsigned f = (uf & 0x7fffff) 

  if (!(e^0xff)) 
   return uf;

  if (!e) 
   return (s << 31) | (f << 1);//f左移1位相当于乘了2

  return (s << 31) | ((e + 1) << 23) | f;//e+1也相当于乘了2
}

 12.floatFloat2Int(uf)

要求:将浮点数 uf 转换成整数。 

运算:Integer/unsigned 相关运算;||,&&,if 和 while 等判断语句。

做法

 浮点数能表示的范围大于无符号整数的范围,我们首先还是取出s、exp、frac,

1.若为0,此时exp=0,frac=0,则返回0.

2.若为最值或NaN则返回1<<31

3.若为非规格化数,若数字很接近于0(此时exp为0,直接与第一个判断合并了),则返回0

4.若为规格化数,先将23位位数变为整数,再判断E为多少,大于31则溢出,小于0则返回0,再根据和23的差调整位数。

int floatFloat2Int(unsigned uf) {
  unsigned s = uf >> 31 & 0x1;
  unsigned e = uf >> 23 & 0xff;
  unsigned f = uf ^ (s << 31) ^ (e << 23);

  //表示为0或者非规格化
  if (!(e | f)) //e=0或f=0时返回0
    return 0;
  
  //Int/NaN
  if (e == 0xff)
     return 1<<31;

  int ee = e - 127;
  f = f | (1 << 23);

  if (ee > 31)
    return 1 << 31;
  else if (ee < 0)
    return 0;

  if (ee >= 23)
  { 
    f <<= (ee - 23);
  }
  else
  {
    f >>= (23 - ee);
   }

 // if (e > 30 + (s & !f)) 
 //   return 0x80000000u;

 // f = ((1 << 23) | f) >> (23 - e);

  if (s) 
    return -f;

  return f;
}

13.floatPower2(x)

要求:使用浮点数表示 2^x。无法表示时:过小返回 0,过大返回 +INF。

运算:Integer/unsigned 相关运算;||,&&,if 和 while 等判断语句。

做法

详细代码如下所示: 

unsigned floatPower2(int x) {
  x = x + 0x7f;
  if (x < 0) return 0;
  return x < 0xff ? x << 23 : 0x7f800000u;
}

 ok就到这,希望能够对你有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值