终于更新了,本次是准备介绍一下csapp的几个实验。因为这学期刚好有这门课,准备借此机会把这本书系统学习一下。
今天的实验环境配置是根据知乎一个用户https://zhuanlan.zhihu.com/p/339047608
配置的。大概注意以下几点:
1、先下载docker desktop;要配置engine,网上有很多办法,但是要注意几点,首先要确保你的电脑cpu支持虚拟化,默认是不支持的,这个可以在开机bios修改;然后要把windows功能hyper打上勾。这样才可以配置成功。
2.接下来就是linux环境的设置,可以pull一个镜像,安装好一系列的依赖项。
3、最有意思的一步,文件目录挂载。在linux 虚拟环境中添加一个目录,挂载到你自己电脑的某个目录上,没有这个目录会新建。注意两个目录都必须是根目录,你可以写C:/XXXX,D:/XXXX等。
4、注意每次打开需要先启动和进入这个容器。
然后docker start 容器ID启动我们的容器
输入以下命令进入到这个运行中的容器
docker exec -it 容器id /bin/bash
这个id可以利用 docker ps -a获得。
5 make btest编译
./best运行(注意要先cd 到文件的目录中)
接下来就说一下几个题目,都在bit.c文件中。
1、int bitXor(int x, int y) {
return ~(~x&~y)&~(x&y);
}
这个题目主要是用位操作表示异或,涉及到德摩根定律的推理。要推到只有与没有或,这个是需要一点技巧的。
a^b=
1.(a|b)&(~a|~b)
2.~(~a&~b)&~(a&b)
3.(a&~b)|(~a&b)
2.int tmin(void) {
return 1<<31;
}
让我们写出最小的有符号int ,int32位,1000000....(补码).就是最小的,结果是取反加一,就是2的负32次方
3.int isTmax(int x) {
return !((~(x + 1) ^ x)) & !!(x + 1);//用异或来判断等于!((~(x+1)^x)) 判断这个是否为1即可判断是否为最大值,最大值x=0111 然后x+1之后就会变成1000 我们对1000 取非 0111 就会重新变回x值,一个例外就是x=-1 由于-1=1111 他利用上面的式子判断也符合。挺难想到的
}
4 int allOddBits(int x) {
int a = 0xAA << 8;
int c = a | 0xAA;
int d = c << 16 | c;
return !((x & d) ^ (d)); //求奇数位是否全为1,利用半掩码来算,比较好理解
}
5 int negate(int x) {
return ~x + 1;//A + ~A = -1 和 A + neg A =0 利用这两个式子我们可以得到 neg A = ~A + 1
如何获得一个数的相反数,那就是取反加1
}
6 int isAsciiDigit(int x) {
int lowerBound = 0x30;
int upperBound = 0x39;
int sign = 0x1 << 31; //最小值100000....
return !((x + ~lowerBound + 1) & sign) & !((~x + 1 + upperBound) & sign); //x-30,39-x必须大于0
}
7 int conditional(int x, int y, int z) {
int a = !!(x ^ 0x0); //a=0 if x=0 else a =1
int b = ~a + 1;
int c = ~(y & ~b) + 1;
int d = ~(z & b) + 1;
return y + z + c + d; //如果x != 0 那么 c就会等于0, d=-z, 我们最后就可以返回y 否则我们就返回z
}
int isLessOrEqual(int x, int y) {
int a = x >> 31 & 0x1;
int b = y >> 31 & 0x1;
int c1 = (a & ~b); //表示 x为- y为+
int c2 = (~a & b); //表示 x + y -
int e = y + (~x + 1); // x-y;
int flag = e >> 31; //如果flag 和 c2 不同则说明了溢出了 x-y会产生溢出
return c1 | (!c2 & !flag);
}
int logicalNeg(int x) {
return ((x | (~x + 1)) >> 31) + 1; //如果0,(~x + 1)和x按位或符号位是0,最后结果为1,Tmin和其他数值按位或都为1,-1+1=0
}
int howManyBits(int x) {
int b16, b8, b4, b2, b1, b0;
int flag = x >> 31;
x = (flag & ~x) | (~flag & x); //x为非正数则不变 ,x 为负数 则相当于按位取反
b16 = !!(x >> 16) << 4; //如果高16位不为0,则我们让b16=16
x >>= b16; //如果高16位不为0 则我们右移动16位 来看高16位的情况
//下面过程基本类似
b8 = !!(x >> 8) << 3;
x >>= b8;
b4 = !!(x >> 4) << 2;
x >>= b4;
b2 = !!(x >> 2) << 1;
x >>= b2;
b1 = !!(x >> 1);
x >>= b1;
b0 = x;
return b0 + b1 + b2 + b4 + b8 + b16 + 1;
}
unsigned floatScale2(unsigned uf) {
unsigned exp = (uf & 0x7f800000) >> 23; //23-30 这8位
unsigned sign = uf >> 31 & 0x1;
unsigned frac = uf & 0x7FFFFF;
unsigned res;
if (exp == 0xFF)return uf; //如果exp=255 并且尾数非0 就是NaN 直接return 就好 其次如果frac 全为0 那么则表示无穷大 这两种情况都可以直接return
else if (exp == 0) {
frac <<= 1; //如果exp=0 则表示非规格化数 那么我们直接返回uf*2 就可就是把frac>>1
res = (sign << 31) | (exp << 23) | frac;
}
else {
exp++;
res = (sign << 31) | (exp << 23) | frac;
}
return res;
}
int floatFloat2Int(unsigned uf) {
unsigned exp = (uf&0x7f800000)>>23;
int sign=uf>>31&0x1;
unsigned frac=uf&0x7FFFFF;
int E=exp-127;
if(E<0)return 0;
else if(E >= 31){
return 0x80000000u;
}
else{
frac=frac|1<<23;
if(E<23) {//需要舍入
frac>>=(23-E);
}else{
frac <<= (E - 23);
}
}
if (sign)
return -frac;
else
return frac;
}
unsigned floatPower2(int x) {
if(x>127){
return 0xFF<<23;
}
else if(x<-148)return 0;
else if(x>=-126){
int exp = x + 127;
return (exp << 23);
} else{
int t = 148 + x;
return (1 << t);
}
}、、从这个题可以看出单精度(32位)浮点数能表示的数字的范围
最大值是norm形式,exp为254(再大就是NaN和INF了),frac的每一位全为1(虽然在这题里不是这样),就能得到(2−ϵ)127
最小值是denorm形式,exp为0,frac为1,此时指数e是1-127,尾数额外提供了23位的指数,这样就得到2−149
这样直接做就可以了
浮点数这几个题看似很难,其实弄懂它的规则之后还是很好理解的,不像前面有的题很难想到。
可以看到最后结果全对。其中最后一个题目一直时间限制出错,原因是btest.c文件把时间限制在10秒内,改成大一点就可以。