DataLab
近来开始读 CS:APP3e 第二章,但干看书做课后题太乏味,于是提前把 DataLab 拉出来练练。不一定是优解,趁热记录一下思路吧。
如果读者是那种还没做完 lab 就想借鉴答案的,还请收手,坚持独立完成吧,正如课程作者所说,
Don't cheat, even the act of searching is checting.
bitXor
1 | /* |
简单的公式可以写作 (x & y) | (~x & y)
,但题目要求只能用 ~ & 两种操作,换句话就是考察用 ~ & 来实现 | 操作,和逻辑与或非类似。
tmin
1 | /* |
这个题目就是计算出 0x80000000
,基本的移位操作即可,不用复杂化。
isTmax
1 | /* |
上面已经知道怎么获取 TMIN,TMAX 可以用 ~TMIN 表示,因此主要考察两个数是否相等 —— ^
。
错误更正
感谢 @nerrons 兄指正
前面的解法忽略了操作符的限制,是不合题意的。故更换思路:由于 TMAX + 1 可得到 TMIN,若 x 为 TMAX,则 x + 1 + x 结果为 0。
但直接这样写无法通过检测程序,是因为 0xffffffff 同样满足 x + 1 + x 为 0 的特性,需要将该情况排除。
1 | int isTmax(int x) { |
allOddBits
1 | /* |
先构造 0xAAAAAAAA
,利用 & 操作将所有奇数位提出来,再和已构造的数判等。
negate
1 | /* |
二进制基础扎实的话,可以秒出结果。
isAsciiDigit
1 | /* |
主要思路可以用逻辑运算表示,(x - 0x30 >= 0) && (0x39 - x) >=0
,这里新概念是如何判断数值是否小于 0。
conditional
1 | /* |
这里我用 ~(!x) + 1
构造了 x 的类布尔表示,如果 x 为真,表达式结果为 0,反之表达式结果为 ~0。
isLessOrEqual
1 | /* |
核心是判断 y + (-x) >= 0
。一开始我做题时被 0x80000000
边界条件烦到了,所以将其考虑进了判断条件。
具体做法是判断 Y 等于 TMIN 时返回 0,X 等于 TMIN 时返回 1。此外也考虑了若 x 为负 y 为 正返回 1,x 为正 y 为负返回 0。
这样想得太复杂了,使用的操作有点多,而题目对 ops 限制是 24,担心过不了 dlc 的语法检查。 所以又花更多时间想出更简单的方法。用逻辑操作可以写作 (y >=0 && x <0) || ((x * y >= 0) && (y + (-x) >= 0))
。不过我后来在 linux 上运行了一下第一种方法,dlc 并没有报错。
logicalNeg
1 | /* |
x 小于 0 时结果为 1,否则检查 x + TMAX
是否进位为负数。
howManyBits
1 | /* howManyBits - return the minimum number of bits required to represent x in |
这里我利用了之前 conditional 的做法,讲 x 为负的情况排除掉,统一处理正整数。统计位数可以采取二分法查找最高位的 1,但做了几轮测试就会发现二分法存在漏位的问题。
不过这只在偶数位发生,奇数位不受影响。因此为了排除这个影响,我暴力地用 x |= (x << 1)
的办法让最高位的 1 左移 1 位。
floatScale2
1 | /* |
只需要简单地取出指数部分,甚至不需要拆解,排除 INF、NaN、非规格化的情况之后,剩下规格化的处理是指数部分的位进一。
floatFloat2Int
1 | /* |
首先拆分单精度浮点数的指数和基数,指数部分减去 127 偏移量,用来排除临界条件。大于 31 时,超过 32 位 Two’s Complement 的最大范围,小于 0 则忽略不计,根据题意分别返回 0x80000000 和 0。
之后根据指数部分是否大于 23 来判断小数点位置。如果大于,说明小数部分全部在小数点左边,需要左移;如果小于则需要右移。最后补上符号位。
floatPower2
1 | /* |
加 127 得到指数阶码,超过表示范围则返回 0 和 INF。由于小数点后面都是 0,只需左移指数部分。
小结
现在 Mac 已无法运行 32 位的代码检查工具 dlc,不过可以先跑逻辑测试,等写完再放到 Linux 机跑一遍 dlc 测试。
原以为这点知识在学校掌握得还可以,随书习题和前几道 lab 也的确简单,实际做到后面有许多卡壳的点,浮点数的概念都模糊了,真是一边翻书一边做,快两天才完成。书本的这章我还是甭跳了,继续刷去吧。