CSAPP datalab详解

注:这些实验都是自己写的,所以有很大的优化空间,有更好答案的小伙伴们欢迎在底下留言或者私信我哦。

bitXor(x,y)

只使用两种位运算实现异或操作。这个算是一个比较简单的问题了,难度系数1。

解题思路:异或运算是相同的数运算结果为0,否则为1。本题采取的思路是计算两个数,第一个数x,y该位同时为0该位为0,其他位为1。第二个数x,y该位同时为1时该位为0,其他位为1。再将两个数进行&运算,即可得到最终运算结果。

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

tmin()

使用位运算获取对2补码的最小 int 值。这个题目也是比较简单

解题思路:这题没什么好说的,最小int值是0x80000000,1左移31位即可。

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

isTmax(x)

通过位运算计算是否是补码最大值

解题思路:补码最大值是0x7FFFFFFF。观察这个数的形式,我们发现加1后,这个数就变成了补码最小值,而这个数与其位数全部取反是一样的。所以我们可以通过验证这个数加1的值与其全部位取反之后值的相等关系来判断是否是最大值。而异或就代表了相等关系。但我们要排除一个特殊情况,即0xFFFFFFFF加1后也与其位数全部取反是一样的。排除了这个特殊情况之后,我们就能得到正确答案了。
int isTmax(int x) {
return (!((x + 0x01)^(~x) )&!!(x+1))&0x01;
}

allOddBits(x)

判断所有奇数位是否都为1,这里的奇数指的是位的阶级是2的几次幂。重在思考转换规律,如何转换为对应的布尔值

*解题思路:*判断所有奇数位是否为1,可以采取迭代的方法。具体迭代格式如代码所示,其基本思想是所有奇数位的数&运算,最后将该数存储在第二位的数中。判断此时第二位数是否为1,只有当所有奇数位为1时,第二位数才为1,否则为0,以此作出判断即可。

int allOddBits(int x) {	
  x &= (x>>16);
  x &= (x >> 8);
  x &= (x >> 4);
  x &= (x>>2);
  return !((x & 0x2)^0x2);
}

negate

不使用 - 操作符,求 -x 值。这个题目是常识。
不懂的小伙伴可以看一下补码和反码的关系。

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

isAsciiDigit

计算输入值是否是数字 0-9 的 ASCII 值。

解题思路: 本题就是判断x在0x30 和0x39范围之间。首先利用右移判断第二个字节是否为3,这是基本条件。然后判断低字节,采取的判断方式为判断最高位是否为1,若为1则不属于(此时需要排除掉8和9两个情况)。即可解出本题。

int isAsciiDigit(int x) {
	int y = x&0xF;
   	return (!((x >> 4) ^ 0x3)) &(!(y ^ 0x9) | !(y >> 3) | !(y^0x8));
	//&(!(x^(x&0xFF)));
}

conditional

使用位级运算实现C语言中的 x?y:z三目运算符。又是位级运算的一个使用技巧。

解题思路:三目运算符的效果大家应该都很清楚。这道题可以考虑将x转换为全0或者全1,再利用&,|运算决定输出的是y还是z。

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

isLessOrEqual

使用位级运算符实现<=
解题思路:这道题是判断x和y的大小。基本思想是用减法判断,首先计算出x-y的结果(利用取反+1)。然后把x,y以及减法结果右移31位来获取其正负性。然后分情况讨论,第一种是x-y<0,且x,y符号相同(防止产生溢出)。第二种是x,y正负性相异,此时应该是x<0,y>0。第三种是,当y位TMin时,x一定<=y,但此时前两种情况无法判断,需单独讨论。第四种是x和y相等,用异或便可以解决。

int isLessOrEqual(int x, int y) {
	int xy = x + (~y + 1);
	int sxy = xy >> 31; 
	int sx = (x >> 31) ;
	int sy =(y >> 31) ;
	return (!!sxy & !(sx ^ sy)) | ((sx ^ 0x0) & !(sy ^0x0)) |  !(x^y) & !(!(y << 1) & (y ^0x0));
}

logicalNeg(x)

使用位级运算求逻辑非 !
解题思路: 本题的思路和前面判断全部奇数位为1有点像。利用迭代将1全部都集中在最高位,|运算代表只要有一个为1,就为1。再右移31位,因为是算术右移,再加1即可。

int logicalNeg(int x) {
	x |= (x << 16);
	x |= x <<  8;
	x |= x << 4;
	x |= x << 2;
	x |= x << 1;
	return ((x >> 31) + 1); 	
}

补充:
这题可能写复杂了,我们只要保证x为0时,最高位为0。x不为0时,最高位为1即可,所以可以用x与其相反数取或运算,因为正负性相异,所以最高位一定为1。而0不管负数与否,最高位都是0。要注意的一点是,当x为TMin时,其最高位也不变,但是它的最高位就是1,所以没有影响。

  return ((x|(~x+1))>>31)+1;

howManyBits

求值:“一个数用补码表示最少需要几位?”
解题思路:我们如果用最少的位数表示负数,那么它符号位之后一定是0。我们可以采取对负数取反的方式,将其和正数统一起来,用相同的方式计算最少表示位数。(符号位变成0,有效位数最高位变成1)。然后利用迭代的方法进行判断,首先判断高16位是否有1,判断方法是将x右移16位,若此时x为0,则说明高16位无数。否则说明x高16位有数,此时x肯定包含低16位,所以表示位数加上16,再将x右移动16位,判断高16位的情况。以此类推,直到判断最高两位时,右移1位,最高位是1,则加1,否则加0,但原本应该加2,加1的,所以少加了一位。再加上符号位,最后应该再加上2。然后对特殊情况-1,0单独处理即可得到最后答案。

int howManyBits(int x) {
  int shift1,shift2,shift4,shift8,shift16;
  int sum;
  int t = ((!(x) << 31)>>31);
  int t2 = (!(~x)<<31)>>31;
  int op = x ^(x >> 31);
  shift16 = (!!(op>>16)) << 4;
  op = op >>shift16;
  shift8 = (!!(op >> 8)) << 3;
  op = op >> shift8;
  shift4 = (!!(op>>4)) <<2;
  op = op >> shift4;
  shift2 = (!!(op >> 2)) <<1 ;
  op >>= shift2;
  shift1 = (!!(op>>1));
  op = op >> shift1;
  sum = 2 + shift16 + shift8 + shift4 + shift2 +shift1;  
  return (t2 & 1) | ((~t2) & (t & 1)) | ((~t)&(~t2)&sum);
}

floatScale2

求2乘一个浮点数
*解题思路:*这道题考察的是浮点数怎么表示的。浮点数成乘2,首先想到的是左移一位。但是浮点数在exp是否等于0时有两种表示方式,所以需要分别考虑。当exp=0是,对于小数部分最高位不为1的时候,简单左移一位即可,但最高位为1时,左移一位会超出整数的表示范围,但我们发现这个数可以用exp+1来表示,于是就用exp+=f>>22来表示指数。当exp不等于0时,对exp不等于0的情况,我如果exp<255,那么对exp简单加一。若exp=255,直接返回原本的数字即可。

unsigned floatScale2(unsigned uf) {
  int sx = (uf >>31);
  int exp = (uf >> 23) & 0xFF;
  int f = uf &(0x7FFFFF);
  int neg = !(exp ^ 0xff);
  //int neg1 = !(exp ^0xfe);
  if((!neg) ){
	  if (!(exp ^ 0)){
		  exp += f >> 22;
		  f <<= 1;
		  f = f & (0x7FFFFF);
	  }
	  else{
		 exp += 1;
	  }
  }
  return (sx << 31) | (exp << 23) |f ;
}

floatFloat2Int

将浮点数转换为整数
*解题思路:*首先提取浮点数的符号位,指数部分和小数部分。浮点数转整数都是直接把尾数舍掉(浮点数有23位有效数字,整数有32位有效数字)。考虑四种情况,exp<127时,此时指数<=0,整数部分为0,舍掉小数部分,直接得到0。当exp>=127 && exp <=150时,指数大于等于0,小于等于23,说明浮点数的仍然有有一部分是小数,舍掉小数部分。当exp>=151&&exp<=157时,指数在24~30之间,此时浮点数全部为整数,且没有超出int表示的范围(注意此时浮点数的小数部分前要多一个1)。指数再大就超出范围,返回0x80000000即可。

int floatFloat2Int(unsigned uf) {
  unsigned sx = uf >> 31;
  int exp = (uf >> 23) & 0xFF;
  int f = uf&(0x7FFFFF);
  if(exp  < 127){
	return 0;
  }
  else if(exp >=127 && exp <=150){
	 f |= 0x800000;
	 f = f >> (23 - (exp - 127));
  }
  else if(exp >= 151 && exp <=157){
	f |= 0x800000;
	f <<= (exp - 127);
  }
  else{
	return 0x80000000;
  }
  if (sx){
	return -f;
  }  
  else{ 
	return f;
  }
}

floatPower2(int x)

求2^x
*解题思路:*分情况讨论。当exp=0时,它可以表示2的负数次方,这个数是用小数位来表示。float的小数位一共有23位,所以一共可以表示23个数字,范围(次方)是(-23-126)~(-1-126),它小数1所在的位置可以用23 - (-x - 126)计算出来。当exp!=0时,它必须让f的小数位全部置为0,表示的范围(次方)是(-126,127),exp=x+127。大于127时,超出了范围,直接返回固定值即可。

unsigned floatPower2(int x) {
	int exp = 0;
	int f  = 0;
	if(x < -149){
		return 0;
	}
	else if(x >= -149 && x <-126){
		f = 1 << (23 - (-x - 126));
		exp = 0;
	}
	else if (x > 127){
	         return 0x7F800000;
		 }

	else if(x >= -126){
		exp = x + 127;
		f  = 0;
	}
	return (0<<31) | (exp <<23) |f;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值