CSAPP dataLab实验笔记

因为实验环境是unix ,所以要搭环境 我用的是docker 非常方便

这边建议参考学姐的博客 总结的非常详细

https://blog.csdn.net/weixin_52259822/article/details/123610165

不过就是遇到了make: command not found的问题 当使用yum -y install gcc automake autoconf libtool make解决时候

显示 mirrorlist: No URLs in mirrorlist

后来查了发现Centos8于2021年年底停止了服务,大家再在使用yum源安装时候,出现下面错误“错误:Failed to download metadata for repo ‘AppStream’: Cannot prepare internal mirrorlist: No URLs in mirrorlist” 解决方法如下:

https://cloud.tencent.com/developer/article/1993317

如果出现bash: wget: command not found 的问题

yum -y install wget

1.bitXor

只使用~和& 实现 ^

学过离散的话,我们可以知道 a^b = (a&~b) | (~a&b)

那么此时只需要将 | 转换。这里就需要用到德摩根公式了:

CSAPP dataLab实验笔记_第1张图片

总的推导:

CSAPP dataLab实验笔记_第2张图片

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

2.tmin

获取对2补码的最小 int 值

审题,注意是补码的最小值,那么可知符号位为1 其余为0即可

这里用移位运算实现

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

3.isTmax

查看是否是补码最大值

我们知道,补码的最大值是01111... 可以发现 补码最大值+1 == 补码最小值(补码最大值取反)但要特判下,如果值为0的话也是满足这个条件的,所以我们要把0的情况单独拎出来:

这里可以用 !!x 来判断x是否有值

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

4.allOddBits

判断是否所有奇数位为1 偶数位为0

这里想的是构造奇数位为1 偶数位为0的数,然后两者取异或对比即可

int allOddBits(int x) {
	//1010 1010
	int m = (0xAA << 8) | 0xAA;
	m = (m << 16) | m;
  return !((x & m) ^ m);
}

5.negate

对x取反,之前题目做过

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

6.isAsciiDigit

让我们判断一个数是否位于0x300x39之间。

这道题方法是0x39 - x >=0 ,0x30 - x <=0 判断正负就行,不过刚开始没处理好第一反应是用异或

int isAsciiDigit(int x) {
	int a = 0x39 + (~x + 1); // 0x39 - x >=0
	int b = 0x30 + (~x + 1); // 0x30 - x <=0
	int c = 0x1<<31;
	int m = a&c;
	int n = b&c;
	// m^n
  return !m&!n;
}

7.conditional

实现三目运算符 x ? y : z 

只要x不为0,我们就返回y;否则返回z

根据做过的题其实可以很容易就做出来,不过这里比较麻烦的是如何构造if else的条件

这里巧妙的是我们可以用0和1的&关系做出:

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 ≤ 0 但是这里要考虑符号的正负问题:当 x 和 y 异号的时候,有可能溢出。所以我们先判断符号位,同号的话就用 x - y ≤ 0 , 异号的话只有x为正时才满足题目要求。这里的话先判断x和y是否等于,然后再分符号位情况考虑。这里&1是方便格式化。

int isLessOrEqual(int x, int y) {
	//取符号位 
	int a = x>>31&1;
	int b = y>>31&1;
	//判断同异号 
	int isab = !!(a ^ b);
	// x - y
	int k = x + (~y + 1) >> 31;
	int m = (k >> 31)&1;
  return !(x ^ y) | (m & !isab) | (isab & !!x);
}

9.logicalNeg

让我们实现!运算符 当x等于0时候 返回1 反之返回 0

还是受到前面写的题的启发,找特殊点:只有0以及补码的最小值的相反数为其本身。至于如何区分0和补码最小值呢,我们可以巧妙地用两者的补码的符号位来判断。

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

10.howManyBits

判断一个数在二进制补码表示下,最少需要多少位才能表示出来

我们看给出的样例:12(01100) =  5  可以看出 当x为正数时,我们需要从最高位找到第一个1的位然后+1 , 当x为负数时,我们只需找到最高位数即可。因为不能用for循环,所以推荐用二分的方法去做:先看16位 ,再看8位,4位,2位。。。这里用了一种很巧妙的方法判断正负号,使得我们可以统一寻找最高位为1的位。

int howManyBits(int x) {
	//二分
	int a = x>>31;
	//如果x为正的话 不变 为负的话 负数的最高位同样为1 
	x = (a & ~x) | (~a & x);
	//!!(x>>16)来判断位数上是否有1 
	int b_16 = !!(x>>16)<<4;
	//如果有 则将x右移16位 
	x = x>>b_16; 
	
	int b_8 = !!(x>>8)<<3;
	x = x>>b_8;
	
	int b_4 = !!(x>>4)<<2;
	x = x>>b_4;
	
	int b_2 = !!(x>>2)<<1;
	x = x>>b_2;
	
	int b_1 = !!(x>>1);
	x = x>>b_1;
	
	int b_0 = x;
	
	
	//别忘了 +1 !!! 
  return b_16+b_8+b_4+b_2+b_1+b_0+1;
}

11.floatScale2

浮点数可以拆分成三部分:符号位s,阶码exp,小数frac

注:M是二进制小数!

如下图:

CSAPP dataLab实验笔记_第3张图片

其中浮点数由阶码决定了是什么类型:

1.规格化:当阶码二进制不全为0且不全为1时 也就是1.0101010这样的

2.非规格化:当阶码二进制全为0时 也就是0.0101010这样的

3.特殊值:当阶码二进制全为1时,分为无穷小,无穷大(阶码全为1小数全为0),NaN

首先判断是否为0时,因为在浮点数中正0和负0是有区别的,所以我们也直接返回参数。再判断是否为无穷大或NaN 当为无穷或NaN,直接返回参数。当剩下的情况,分为非规格化和规格化判断,当为非规格化时,只有小数位,像0.0101....这样,只考虑frac即可。当为规格化时,根据IEEE公式,要expr + 1 ,然后 这样就相当于乘以了2(因为expr是2的次方)


unsigned floatScale2(unsigned uf) {
    // s expr frac
    unsigned s = (uf >> 31) & 0x1;
    unsigned expr = (uf >> 23) & 0xFF;
    unsigned frac = uf & 0x7FFFFF;
    
    //0
    if (expr == 0 && frac == 0) {
        return uf;
    }
    //无穷 or NaN
    if (expr == 0xFF) {
        return uf;
    }
    //非规格化
    if (expr == 0) {
        //E = expr - 127 = -127
        //frac
        frac = frac << 1;
        return (s << 31) | frac;
        
        
    }
    
    //规格化
    //E = expr - 127
    expr = expr + 1;
    //根据IEEE
    return (s << 31) | (expr << 23) | frac;
    
}

12.floatFloat2Int

单精度浮点数转换为int

这里我们还是分多种情况考虑:先判断是否为 0 。再判断是否无穷 or NaN 。紧接着如果是非规格化的话,expr = 0 , E = expr - 127 = -127 ,根据IEEE可知 ,(-1)^s * M * 2^E ,所以值太小了,在int里为0。如果是规格化,这里有个巧妙的理解方法,我们通过 E 来决定小数点的位置,当E大于等于23时(因为小数位只有23位)可以看成二进制数往右再移(E - 23)位 反之二进制数往左移动(23 - E)位 舍弃(23 - E)位的数。最后要特殊判断一下,溢出和得到的值小于1和得到的值为负数的情况。

int floatFloat2Int(unsigned uf) {
    // s expr frac
    unsigned s = (uf >> 31) & 0x1;
    unsigned expr = (uf >> 23) & 0xFF;
    unsigned frac = uf & 0x7FFFFF;
    
    //0
    if (expr == 0 && frac == 0) {
        return 0;
    }
    //无穷 or NaN
    if (expr == 0xFF) {
        return  1 << 31;
    }
    
    //非规格
    if (expr == 0) {
        //E = expr - 127 = -127
        //(-1)^s * M * 2^E
        return 0;
    }
    
    //规格化
    int E = expr - 127;
    frac = frac | (1 << 23);
    //当return大于32位的时候会爆掉(E>31) 当return小于0的时候为0(E<0)
    if (E > 31) {
        return 1 << 31;
    }else if(E < 0){
        return 0;
    }
    //当E >= 23 可以看成二进制数往右再移(E - 23)位 反之二进制数往左移动(23 - E)位 舍弃(23 - E)位的数
    if (E >= 23) {
        frac = frac << (E - 23);
    }else{
        frac = frac >> (23 - E);
    }
    //负数
    if (s){
        return ~frac + 1;
    }
    
  return frac;
}

13.floatPower2

用单精度浮点数表示2的x次方

这道题要注意x的范围,当数为非规格化时,什么时候x最小呢?我们可以从(-1)^s * M * 2^E考虑,其实这里我们可以舍去s,因为总是为正数,考虑M * 2^E ,M的最小值是2 ^ -23 ,E最小值为 -126 , 所以x最小值为 -23 - 126 = -149 ,而M * 2 ^ E 最大值为 二进制所有位数为 1 ,因为是相乘的,所以最大值为 -126。当数为规格化时,-126次方(E = exp - bias)是x的最小值。

unsigned floatPower2(int x) {
    if (x < -149) {
        //值小返回0
        return 0;
    }else if (x > 127){
        //值大返回无穷
        return (0xFF) << 23;
    }else if (x < -126){
        //E = -126
        return 1 << (23 + 126 + x);
    }else{
        return (x + 127) << 23;
    }
    
}

做完之后对位运算有了更深刻的理解,收获非常大。

你可能感兴趣的:(csapp,笔记)