csapp datalab

Posted Y0n1an

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csapp datalab相关的知识,希望对你有一定的参考价值。

异或运算

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

题目要求用~和&表达异或,异或的定义算术表达式是(a&~b)|(~a&b),可以这样理解方便记忆:b原来为0的位取反变成1,这样和a为1的位进行按位异或就可以得到1,但是还有另一种情况就是a为0的位和b为1的位,然后将这两种情况取并集就得到了
然后推导另外两种,主要是德摩根定理的应用

(a&~b)|(~a&b) = (a|~a)&(a|b)&(~a|~b)&(b|~b)
              = 1&(a|b)&(~a|~b)&1
              =(a|b)&(~a|~b)
              =~~(a|b)&~~(~a|~b)
              =(~(~a&~b))&~(a&b)

32位最小值

int tmin(void) 
  return 1<<31;

这个直接左移,后面为0,就是书上说的-2^(w-1) +0

位最大值

int isTmax(int x) 
  return !(~(x+1)^x)&!!(x+1);

比如0111这类如果加1就是1000,和原来的位是相反的,所以直接异或,异或结果为0就是相同的,代表就是0111这类数,但是-1也满足条件,所以我们要与上一个!!(x+1),也就是取交集

奇数偶数位

int allOddBits(int x) 
  return !((x&0xAAAAAAAA)^0xAAAAAAAA);

判断是不是和A:1010类似的就行了

取负数

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

补码加1,没什么好说的

判断是否在范围内

int isAsciiDigit(int x) 
  int a = !(x>>4 ^ 0x3);
  int b = x & 0xf;
  int c = ~0xA + 1;
  int d = !!((b+c) & 0x8000);
  return a&d;

分思路的话就是分两步,第一是判断0x39和0x30的倒数第二个字节,所以右移4位后如果是3就把a置为1
然后取最低字节,并且加上-0xA,如果小于0,就在范围内,判断小于0就判断最高位是否为0就行了

三目运算符

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

x为0,返回z,x非0返回y
所以让x非两次,如果非零的话a就是32个1,是0的话两次非+1溢出后就是32个0
所以用一个特定变量flag来控制返回y还是返回z就用这个固定的套路(flag & x)|(~flag & y),让flag因为两种情况,所以取并集

比较大小

int isLessOrEqual(int x, int y) 
  int flag = !(x >> 31) ^ !(y >> 31);
  int a = flag & (x>>31); 
  int b = !flag & !((y+(~x+1))>>31);
  return a | b;

如果x > y 返回0,若x <=y 返回1
首先判断x,y是否异号,如果异号,只要x的符号位为1,那就肯定小,就直接返回1就可以了
还有一种情况就是同号,那就还是让y加上x的补码,判断符号位,flag用来判断是否同号,两者取交集。

逻辑非

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

int logicalNeg(int x) 
  return !(~(!!x)+1);

(~(!!x)+1)

两种方法
第二种写法还是非0与0的问题,也可以用和上面三目运算符一样的方法,这里说一下第一段如果是0的话,0和自己本身补码的符号位是相同的,都是0,所以让他置为1,其他的加1后由于是算术右移,溢出后变0

多少位

int howManyBits(int x) 
  int flag = x >> 31;
  x = (flag & ~x)|(~flag&x);//正数不变,负数取反
  int b16 = !!(x>>16)<<4;//判断第十六位是不是0,如果不是,说明高16位上存在最左边的1,并且为后面移动16位作准备
  x>>=b16;/*根据16位的情况判断是否移动*/
  int b8 = !!(x>>8)<<3;
  x>>=b8;
  int b4 = !!(x>>4)<<2;
  x>>=b4;
  int b2 = !!(x>>2)<<1;
  x>>=b2;
  int b1 = !!(x>>1);
  x>>=b1;
  int b0 = x;
  return b16+b8+b4+b2+b1+b0+1;//位长是最左边1再加上1

这就是二分法查找,找位数就是找最左边的1,但是负数要取反,就用符号位flag来控制,上面说过的那个模板,如果16位是1,那就要往它高位再找看看有没有1,如果不是1,那就不移动,往8位找,下面都是一样的

浮点数乘2

unsigned floatScale2(unsigned uf) 
  unsigned frac = uf & 0x007fffff;
  unsigned exp = (uf >> 23) & 0xff;
  unsigned s = uf >> 31;
  if(!exp)
    frac <<= 1;
    if(frac >> 23)
      frac = frac & 0x007fffff;
      exp++;
    
  
  else if(exp!=0xff)
      exp++;
      
  return (s<<31)|(exp<<23)|frac;

这里分为几种情况
第一种 规格化数
exp不为0
第二种 非规格化数
exp为0
第三种 特殊值
exp全1,frac不为0,为NAN,f为0则是无穷大
那f左移就是乘2,如果溢出那就exp加1就行了

浮点数转换

int floatFloat2Int(unsigned uf) 
  int frac = (uf & 0x007fffff) | 0x00800000;
  int exp = ((uf>>23) & 0xff) - 127;
  int s = uf>>31;
  int tar = 0;
  if (s)
    s = -1;
  else
    s = 1;

  if (exp > 31)
    return 0x80000000;
  if (exp < 0)
    return 0;
  if (exp > 23)
    tar = s * (frac << (exp - 23));
  else if (exp < 23)
    tar = s * (frac >> (23 - exp));
  return tar;

还是分为三种情况,e为255即返回0x80000000,这里就是exp = 255-bias,因为int最大表示范围是2^31,这里因为M为1~2-ipx,所以E小于2^31次方
如果e小于0返回0
如果是规格化数,有公式 V = (-1)^s*M*2^E M = 1 + frac
所以第一步就把24位置为1了,补上了1
如果小于23,就要把23-E位舍去,因为最小规格数是2^-126,frac有23位,所以如果小于23的,通过f和exp之间的差值相互相乘抵消为1。
如果大于23,再乘抵消后剩余的就行了

边界判断

最小非规格数:exp = 0 ,frac = 00……01, E = 1-bias 2^-23*2^-126
最大非规格数:exp = 0, frac = 11……11, E = 1-bias (1-i)*2^-126
最小规格数:exp = 0……1,frac = 00……00 ,E = e - bias ,M = 1,1*2^(-127+1)
最大规格数:exp = 1……10,frac = 11……11 ,E = e - bias ,M = 1+frac,(2-i)*2^(-127+(2^8-1-1))

unsigned floatPower2(int x) 
  int exp = x+127;
  if(exp >= 0xff)
    return 0x7f800000;
  if(exp <= 0)
    return 0;
  return exp << 23;

通过上面的实验可以得到,如果把值右移的话过小,可以用非规范化来表示
传入的x是E值,直接加上bias如果过大,直接return题目要求的值
如果小于或等于0,那就是非规格数,返回0,其他值就是规格码左移23位,让它成为2^E返回就行了

以上是关于csapp datalab的主要内容,如果未能解决你的问题,请参考以下文章

csapp datalab

csapp datalab

CSAPP lab1 datalab-handout

CSAPP Data Lab

CSAPP Data Lab

CSAPP实验一:位操作(Data Lab)