本文是笔者完成CSAPP DataLab的一个记录
请访问Lab官网以获得所需资料
注:DataLab中的int和unsigned int都是32位
1. bitXor(x, y)
用~和&实现异或
1 | /* |
主要思路:比较简单的问题,可以考虑到基本结构多半是两块东西取&,并且当x,y同为1或同为0的时候必有其中一块东西是0。
2. tmin()
返回补码表示下int的最小值
1 | /* |
主要思路:这个但凡学过都该会了吧。
3. isTmax(x)
判断x是否为补码表示下int的最大值
1 | /* |
主要思路:Tmax特殊性质就是+1之后溢出到Tmin,然后Tmin的特殊性质就是Tmin=-Tmin
,也即Tmin+Tmin=0
,从而对于Tmax应该满足:(Tmax+1)+(Tmax+1)=0
,也即Tmax+Tmax+2=0
(这里结合律和交换律的成立原书(CSAPP)上有证明,这是一个阿贝尔群)。至此我们获得了一个必要条件,因为除了Tmax以外,还有-1同样满足这个条件,因此我们需要!(x + 1)
来排除掉-1。
4. allOddBits(x)
判断x在二进制表示下奇数位上是否全是1
1 | /* |
主要思路:用到掩码的思想,用0xAA
在二进制下即10101010
,然后对它进行左移,或运算后就可以得到一个32位的奇数位全是1的掩码mask,再用mask和x取&,即可取出x的所有奇数位(即将x的偶数位全部置零)。最后只要用异或判断x的奇数位是否与mask相同(即全是1)即可。
5. negate(x)
得到x的相反数
1 | /* |
主要思想:-x=~x+1
应该十分常见了,以下用 $x>0$的情况举例证明:$w$个 $1$是 $2^w-1$(int下$w$就是32),故对于无符号数 $x$,取反后变成 $2^w-1-x$,再考虑符号位,$~x$即为 $2^w-1-x-2 \times 2^{w-1}=-x-1$,加一即得结果;对于符号位为1的情况,可以类似证明。
6. isAsciiDigit(x)
判断x在不在0~9的ASCII码范围内
1 | /* |
主要思想:这题稍微有些复杂,先写成二进制看看。0x30
是00110000
,0x39
是00111001
。可以观察到,前四位是一样的,都是0011
,也就是说,只要前四位不是0011
话,必定就不在这个范围里。将x右移四位得到前四位,并由此得到第一个约束3^(x>>4)==0
。其次看后四位,最小值是0000
不用管,最大值是1001
,加上0110
后就得到了1111
,也就是说,如果后四位大于1001
的话,加上0110
后必定会进位,只要检测是否进位即可。又由于x的5、6位上全是1,一进就进到第七位,所以就得到(x+6)>>6==0
的约束条件。
7. conditional(x, y, z)
纯位运算实现条件分支
1 | /* |
主要思路:似乎是比较常见的东西。仍然涉及到掩码思想,如果能得到二进制下所有位全1或全0的掩码,和z取&就可以得到z或0了。这里获取全1掩码的过程利用了算数右移的性质,即会将最高位填充成符号位的值,只要算数右移31位,就可以得到全1的掩码了。
8. isLessOrEqual(x, y)
位运算实现$\leq$
1 | /* |
主要思路:第一种方法比较优秀,思路是计算出y-x,然后判断其符号。但由于int有溢出的问题,所以应该先判断x,y是否同号,异号可直接确定,同号再进行相减(这时不会溢出)。第二种方法用的操作符有点多(是我最开始的愚蠢思路qwq),思路是异或取出x和y不相同的位,再取出异或结果的最高位(因为最高位决定大小),哪个数字里有这一位哪个就是较大的数(主要是取最高位感觉没什么好办法)。
9. logicalNeg(x)
位运算实现
!
1 | /* |
主要思路:只有0和Tmin取相反数后符号位不变,并且0是0而Tmin是1;更进一步,只有0符号位在操作前后一直是0。而由于算术右移的特性,我们会得到-1或0,故+1变成0或1。
10. howManyBits(x)
计算出用二进制补码表示x需要多少位
1 | /* howManyBits - return the minimum number of bits required to represent x in |
主要思路:这题困扰了我相当长的时间,比较棘手。思考一下,对于正数而言,只要在最高位的1前面加一个符号位0即可,对于负数而言,只要在最高位的0前面加一个符号位1即可(这里可以考虑一下负数补码下是怎么做位数拓展的,原书中也有相关内容)。然后采用类似倍增的想法,判断高位上是否有1,有就进行右移,同时记录下右移的总位数。
这道题没有注释部分的答案来自@李明岳的一篇专栏](https://zhuanlan.zhihu.com/p/59534845?utm_source=qq))
下面被注释掉的是笔者的解法,主要思路就是把if (x >> 16) lg2 |= 16, x >>= 16;
翻译成了位运算,劣于上面解法的原因主要是没有考虑到用变量把中间进行的位移一个个记录下来。具体的位运算太丑了,就不进行详细解释了。
11. floatScale2(f)
实现浮点运算的2*f
1 | /* |
主要思路:只要掌握了浮点数的储存结构就没什么难度,分无穷和NaN,非规格化,规格化三种情况处理即可。
12. floatFloat2Int(f)
将浮点数转换成整数
1 | /* |
主要思路:首先通过指数的范围判断是否过大造成int的溢出。若不溢出,由于C中float转int是向零取整,所以只要将多余的小数位直接丢掉即可(注意一下位移的位数不能<0或>31,否则是undefined behavior)。
13. floatPower2(x)
计算2.0的x次幂
1 | /* |
主要思路:主要是区分规格化值是将指数直接放在exp区,非规格化值exp区全是0,只处理很小的数字,所以放在尾码区。注意处理+inf和0即可。
最后
当然是要贴上完成截图啦!
撒花!🌺🌻🌼🌷🌹
受笔者知识水平和表达能力所限,有的问题难免解释得或啰嗦或出现疏漏,欢迎在评论区指正