本贴是本人记录做CSAPP lab全过程,分享自己的思路,与解题过程。
做lab1过程感觉自己真的还是太菜了,除了几个简单的题,好几个都没思路,看了别人的思路才写出来,希望自己学完CSAPP可以厉害些。
1.只通过~ 和 &实现^操作
int bitXor(int x, int y) {
return ~((~x)&(~y))&(~(x&y));
}
思路:分别计算x与y的非0相同段和非1相同段,然后使用&即得到不同段,即异或结果。
2.输出int类型的tmin
int tmin(void) {
//i need return 100.....000
//because int is 32 bit so
return 1 << 31;
}
思路:这题很简单,只需要把1左移31位就行。(忽略我的注解。。。。。)
3.判断一个数是不是TMax
int isTmax(int x) {
return !(x ^ (~(1 << 31)));
}
思路:首先我们要求得Tmax(~(1<<31)),然后如果这个数等于TMax,那么异或操作结果为0(每个位都相同),否则不为0(肯定有一个位不同),然后使用!取反即可得到符合条件的结果。
4.如果一个数奇数位全为1返回1
int allOddBits(int x) {
int temp = 0xAA;
temp = temp + (temp<<8) + (temp<<16) + (temp<<24);
return !(temp^(x&temp));
}
思路:首先我们需要一个特殊的数,我叫它temp,即一个奇数位都为1,偶数位都为0的数,这样我们就可以忽略偶数位的影响,如果x奇数位全为1,那么x & temp == temp,否则不等于temp,到这一步我们就可以利用上一题异或再取反的思想了。
5.实现-操作
int negate(int x) {
return ~x + 1;
}
思路:这题我秒出答案嘿嘿嘿,之前研究过的内容派上用场了。可以看看我之前写的博客的的内容。
链接:为什么-x = ~x +1,解释在一个小思考题部分。
6.这题就是让你判断一个int值是不是在'0' 和 '9'之间
int isAsciiDigit(int x) {
int a = 0x7fffffff - 57;
int b = -48;
a = a + x;
b = b + x;
a = a >> 31;
b = b >> 31;
return !( a | b);
}
一开始我看到这个题之间懵逼,哪里,位运算还能判断大小吗,我冥思苦想,还是想不出来。。。
后来看了别人的思路,豁然开朗。
首先 '0' = 48,'9' = 57
思路: 我们需要找到2个数,一个数我们叫他a,a + 57 = TMax,再加一就溢出了,符号就反了,利用这个边界性质,我们就可以判断一个数是不是大于57的。第二个数就是 b ,48 + b = 0,不能比48小了,再小符号位也反了,利用这个性质,就可以判断一个数是不是大于等于48,根据返回的结果的符号位,就可以判断了。(只有2个都为0才在范围内)
7.实现 x?y : z
int conditional(int x, int y, int z) {
//一个数与一个全一的数&还是自己,一个数与一个全0的数与则是0,一个数与一个全0的数|还是自己
x = !!x; //把x化为1 或 0
x = ~x + 1; //把x化为全0或全1
return (x&y)|(~x&z);
}
思路:分2步,第一步判断x,第二步消除不满足条件的值的影响(即x=1消除z,x=0消除y),
首先我们怎么消除一个数的影响呢?看我图里的注释和最后一行代码你就懂了。
总思路就是先把x化为全0 or 1,然后消除不需要的y或者z
ps:消除这个思想后面用的挺多的。
8. 判断 x是不是小于y
int isLessOrEqual(int x, int y) {
//首先我们要明确当x y 同号时,x-y是不可能溢出的,但x< 0 y>0 结果为0,x>0 y<0结果为1
//所以我们可以分为溢出与不溢出2种情况
int xfuhao = x >> 31 & 1;
int yfuhao = y >> 31 & 1;
int ifyihao = xfuhao ^ yfuhao;//1异号,0同号
int fuhaoxjiany = !(y + (~x + 1) >> 31 & 1);//1说明y >=x
return (xfuhao&ifyihao)|(!ifyihao&fuhaoxjiany);
}
这题我第一眼看到就想,我靠,这不之前做过(类似的)吗!,然后我就一直想通过之前那个题的思想去做,发现做不出来。。。。。(各位做出来了的可以教教我)
思路:1首先我们就要考虑可能溢出吗?答案是会。但只有x,y异号时会溢出,x,y异号时x y的大小还不好判断吗?只用看x的符号就行了
2不溢出的情况我们直接计算y +(- x)就可以了,至于为什么不用x + (-y)大家可以想想。
然后就又是消除了,当溢出时,要消除不溢出算出的结果,不溢出时要消除溢出的结果。
通过ifyihao这个变量来记录是否异号,通过把他设成111...111或者0000.0000的格式来实现消除.
9.这题是要我们实现!运算符
int logicalNeg(int x) {
//注意到一个性质,除了0和Tmin以外任何数都有相反数,且Tmin取反加1的符号位或也是n一,只有0是0
return ((x | (~x+1))>>31) + 1;
}
思路:这题其实只要你发现这个性质了就好做了。其实就是通过符号位来判断,除了0和tmin,x与x的符号位不同,所以x与-x做异或符号位肯定是1,而Tmin虽然没有相反数,但-Tmin与Tmin异或也是1,所以除了0其他都是1,这正是我们想要的结果.
10.计算表达一个int型的值最少需要多少位
int howManyBits(int x) {
// 00001100 找最高的1
// 11010110 找最高的0
int need16,need8,need4,need2,need1,need0;
int sign = x>>31;
x = (sign|x)&(~sign|~x);//x为非正数则不变 ,x 为负数 则相当于按位取反,顺便消除符号位影响.
need16 = !!(x>>16) << 4;//如果前16位有1,说明至少需要16位,need16 = 16否则need16 = 0
x = x >>need16;
need8 = !!(x>>8) <<3;
x = x >>need8;
need4 = !!(x>>4) <<2;
x = x >>need4;
need2 = !!(x>>2) <<1;
x = x >>need2;
need1 = !!(x>>1);
x = x >>need1;
need0 = !!x;
return need16 + need8 + need4 + need2 + need1 + need0 +1;//1是符号位
}
思路:首先需要明确,对于一个正数,我们需要找到最高位的1,然后加1,对于负数,我们需要找到最高位的0,然后加1.
然后,我们对输入的数进行一个优化,见第三行代码,这样处理后无论是负数还是正数,我们都是求最高位的1,并且去除了符号位的影响。
然后,如何求呢?我们来思考一下,如果前十六位有1,那么是不是意味着后十六位你都需要来表示这个数,如果前十六位的前八位也有1,那么是不是意味着你至少需要后十六位加上前十六位的后八位来表示这个数。。。。。。。如此循环,我们不断缩小范围,然后把最终结果加起来,再加上符号位就是结果了。(特别注意,need0也是有必要的,比如二进制数01)
11.求2×以一个浮点数
unsigned floatScale2(unsigned uf) {
int sign = (uf>>31)<<31;
int exp = uf&0x7f800000;
int frac = (uf&0x7fffff);
if(exp == 0x7f800000) return uf;
else if(exp == 0)
frac = frac<<1;
else
exp = exp + 0x800000;
return sign | exp | frac;
}
思路:分三种情况,特殊值,规格化数,非规格化的数
特殊值:无穷,NaN,这其实是最好处理的,直接看exp的值,是这2种情况直接返回就好.
非规格化数:即exp =0,对于非规格化数,我们让frac左移1位即可,你甚至不用考虑进位,因为IEEE的标准制定者已经实现了平滑过渡,十分舒适.
规格化数:exp + 0x800000即可,exp代表阶数嘛.
最后让返回值的三部分分别取符号位,exp,frac即可.
12.将一个浮点数转为整数
int floatFloat2Int(unsigned uf) {
int sign = (uf >> 31) << 31;
int E = ((uf & 0x7f800000) >> 23) - 127;
int fraq = uf & 0x7fffff;
if (E < 0) return 0;
if (E >= 31) return 0x80000000u;
//说明不越界且是规格化的数
//先把frac的1补上
fraq = fraq | 1 << 23;
if (E >= 23) {
fraq = fraq << (E - 23);
}
else {
fraq = fraq >> (23 - E);
}
if (sign) {
fraq = -fraq;
}
return fraq;
}
思路:一个整数,他的阶最多是31阶,所以大于31阶的直接返回规定的异常值
其次对于阶数为负的,转化为整数舍入后都是0,所以E < 0的都直接返回0
最后剩下的,首先算出他的阶,通过E = exp -127,然后呢给精度位补上隐藏的1,
再看看E与23的大小,如果E < 23,需要舍入,大于的话需要补0,最后加上符号位大功告成.
13. 求2的x次方,并转化成浮点数
unsigned floatPower2(int x) {
//f浮点数的指数w范围为 -127 ~ 128
if(x < -127) return 0;
if(x > 128) return 0xff << 23; //INF最大值
return (x + 127)<<23;
}
思路 :2的x次方对应的就是阶数,而exp的范围是 -127 ~ 128,不在这个范围的,就无法表示了
题目规定太小的返回0,太大的返回正无穷
满足条件的数根据公式E = exp -127 求出exp,然后直接左移23位即可,最后一题不要太爽!!
总结:自己基础打得还是不够牢固,有些题目其实掌握一些性质,很容易就做出来了,有时间的话一定要2刷,接下来就下一个lab见了.