做 datalab 的心路历程总结 1. bitXor 函数 只用 ~ 和 & 实现x ^ y
实现异或,我的思路是:
x
y
x^y
101
100
001
1
1
0
0
0
0
1
0
1
&后为 1,那么两个数该位都是 1
我需要找&后为 0,并且该位只有一个 0(todo)
&后为 0,并且两个数该位都是 0
所以 a & b = c
位为 1 的就找到上面的 1.
~a & ~b = d
位为 1 的就找到上面的 3.
~c & ~d
推出上面的 2.
2. tmin 函数 返回补码表示的最小整数
直接最高位为 1, 其余全 0 即可
也就是-2^31 + ...
最小, 所以…全取 0
3. isTmax 函数 返回是否为补码表示的最大整数
x 如果是 Tmax,有~(x+1) ^ x
这个是 0,再取个!
but x = -1
,这个也满足,需要特判,加上& !!(x+1)
4. allOddBits 函数 若奇数位上全为 1 返回 1,否则 0
原始思路每 8 位检查一下(op 次数超了):
1 2 3 4 5 int allOddBits (int x) { int mask = 0xaa ; return !((x & mask) ^ mask) & !((x >> 8 & mask) ^ mask) & !((x >> 16 & mask) ^ mask) & !((x >> 24 & mask) ^ mask); }
改进思路构造出0xaaaa_aaaa
这个掩码(9 次):
1 2 3 4 5 int allOddBits (int x) { int mask = 0xaa ; mask = (mask << 24 ) | ((mask << 16 ) | ((mask << 8 ) | mask)); return !((x & mask) ^ mask); }
进一步小改进(7 次)
1 2 3 4 5 6 int allOddBits (int x) { int mask = 0xaa ; mask = ((mask << 8 ) | mask); mask = (mask << 16 ) | mask; return !((x & mask) ^ mask); }
笔记 : a == b
等价于 !(a ^ b)
5. negate 函数 返回-x(32bit 补码)
x + ~x = -1
x - x = 0
得-x = ~x + 1
6. isAsciiDigit 函数 若 x 为 ASCII 的数字返回 1,否则 0
思路 1:
判定x - 0x30 >=0
判定0x39 - x >=0
减 x 用到前一题的~x+1
1 2 3 4 5 6 7 8 9 int isAsciiDigit (int x) { int lower_bound = x + (~0x30 + 1 ); int upper_bound = 0x39 + (~x + 1 ); int lower_ok = !(lower_bound >> 31 ); int upper_ok = !(upper_bound >> 31 ); return lower_ok & upper_ok; }
思路 2:
检查 x 如下图
1 2 |24bit |4bit|4bit | |00..00|0011|0 ~ 9|
代码实现
1 2 3 4 5 6 7 8 9 10 int isAsciiDigit (int x) { int all_zero = !(x >> 8 ); int upper_nibble = !(((x >> 4 ) & 0xf ) ^ 0x3 ); int lower_nibble = x & 0xf ; int less_nine = 9 + (~lower_nibble + 1 ); int less_nine_ok = !(less_nine >> 31 ); return less_nine_ok & all_zero & upper_nibble; }
笔记 : 检测 x 是否为负数等价于检查符号位是 0/1
7. conditional 函数 实现仿三目运算符
一开始的错误思路:
1 2 3 4 5 6 7 8 9 10 int conditional (int x, int y, int z) { int y_mask = ~(!x); int z_mask = ~x; int left = (y_mask & y); int right = (z_mask & z); return (left & (!!x)) | (right & (!x)); }
改正后(思路见注释):
1 2 3 4 5 6 7 8 int conditional (int x, int y, int z) { int x_ok = !!x; int y_mask = ~x_ok + 1 ; int z_mask = ~y_mask; return (y & y_mask) | (z & z_mask); }
8. isLessOrEqual 函数 x<=y 就返回 1,否则 0
思路:
9. logicalNeg 函数 实现!运算符
思路:
0 的特征=>0x00..00
全 0
±0 都是本身
x | (-x)
判定符号位是否为 1,转换为式子结果是-1(非 0 时)
1 2 int logicalNeg (int x) { return (((x | (~x + 1 )) >> 31 ) & 1 ) ^ 1 ; }int logicalNeg (int x) { return ((x | (~x + 1 )) >> 31 ) + 1 ; }
10. howManyBits 函数 返回最少用多少位表示 x
思路:
再翻译一下:
先把一个 x 先变为 ~x(x<0 时)
再将这个正数写成 32 位的二进制,需要找到最高有效位 1 的位置(书的课后作业 2.66 类似)
位填充 /位传播
后续问题,怎么统计多少个 1(0x00..111 这样的数)popcount
二分查找
我的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 int howManyBits (int x) { int sign = x >> 31 ; x = (sign & ~x) | (~sign & x); int is_zero = !x; x |= x >> 1 ; x |= x >> 2 ; x |= x >> 4 ; x |= x >> 8 ; x |= x >> 16 ; int mask = 0x55 ; mask = mask | (mask << 8 ); mask = mask | (mask << 16 ); x = (x & mask) + ((x >> 1 ) & mask); mask = 0x33 ; mask = mask | (mask << 8 ); mask = mask | (mask << 16 ); x = (x & mask) + ((x >> 2 ) & mask); mask = 0x0f ; mask = mask | (mask << 8 ); mask = mask | (mask << 16 ); x = (x & mask) + ((x >> 4 ) & mask); int mask_zero = 0x00 ; mask_zero = mask_zero | (mask_zero << 8 ); mask = 0xff ; mask = mask | mask_zero; mask = mask | (mask << 16 ); x = (x & mask) + ((x >> 8 ) & mask); mask_zero = mask_zero | (mask_zero << 16 ); mask = 0xff ; mask = mask | (mask << 8 ); mask = mask | mask_zero; x = (x & mask) + ((x >> 16 ) & mask); int res = x + 1 ; return (res & ~is_zero) | (is_zero & 1 ); }
claude 提供(二分)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int howManyBits (int x) { int sign = x >> 31 ; x = (sign & ~x) | (~sign & x); int is_zero = !x; int bit16, bit8, bit4, bit2, bit1, bit0; bit16 = !!(x >> 16 ) << 4 ; x = x >> bit16; bit8 = !!(x >> 8 ) << 3 ; x = x >> bit8; bit4 = !!(x >> 4 ) << 2 ; x = x >> bit4; bit2 = !!(x >> 2 ) << 1 ; x = x >> bit2; bit1 = !!(x >> 1 ); x = x >> bit1; bit0 = x; int bits = bit16 + bit8 + bit4 + bit2 + bit1 + bit0 + 1 ; return (bits & ~is_zero) | (is_zero & 1 ); }
笔记 :
x = (sign & ~x) | (~sign & x)
保留正 x,负 x 变~x
!!(x >> 16)
判定高 n 位有没有 1
位填充
popcount
11. floatScale2 函数 返回 2*f
思路:
求出 s , exp, frac
分 3 类讨论
规格化
exp++
再拼起来, 如果exp==255
返回 ∞
非规格化
特殊值
个人理解:
规格化的情况(1.frac)
f*2,主要是整数部分*2
exp=>影响的是 E(exp-bias) => 2^E(exp++,E++,对应就是*2 了)
非规格化的情况(0.frac)
f*2,主要是小数部分*2
frac=>影响的是 M(frac)=>(frac<<1,就相当于*2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 unsigned floatScale2 (unsigned uf) { unsigned exp = (uf >> 23 ) & 0xff ; unsigned frac = uf & 0x7fffff ; unsigned s = (uf >> 31 ) & 0x1 ; if (exp == 0xff ) { return uf; } else if (!exp) { return (s << 31 ) | (exp << 23 ) | (frac << 1 ); } else { exp++; if (exp == 0xff ) { return (s << 31 ) | (exp << 23 ); } return (s << 31 ) | (exp << 23 ) | frac; } }
12. floatFloat2Int 函数 返回(int)f
思路:
分 3 类讨论
规格化
E<0 => 0(太小)
E>=31 => 0x80000000u(爆 int)
正常转
非规格化
特殊值
按图示的逆过程:
一开始的写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 E = exp - bias; if (E < 0 ) { return 0 ; } else if (E >= 31 ) { return 0x80000000u ; } else { int point_right = frac >> (23 - E) & 0xffffff ; int point_left = 1 << E; int merge = point_right | point_left; return s ? -merge : merge; }
修正后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int floatFloat2Int (unsigned uf) { int E; unsigned exp = (uf >> 23 ) & 0xff ; unsigned frac = uf & 0x7fffff ; unsigned s = (uf >> 31 ) & 0x1 ; unsigned bias = 127 ; if (exp == 0xff ) { return 0x80000000u ; } else if (!exp) { return 0 ; } else { E = exp - bias; if (E < 0 ) { return 0 ; } else if (E >= 31 ) { return 0x80000000u ; } else { int M = (1 << 23 ) | frac; if (E <= 23 ) { M >>= (23 - E); } else { M <<= (E - 23 ); } return s ? -M : M; } } }
13. floatPower2 函数 返回 2.0^x
思路:
先在稿纸写一下 2.0 的表示和 4.0 的表示,发现 exp 部分(从 2=>4)+1 即可
规格数: exp 范围在 1-254,找出范围(x >= -126 && x <= 127)
非规格数: frac 里每个位都可以是 1
说实话一开始也不知道(x >= -149 && x < -126)这个范围怎么处理
这个范围还是用./btest -f floatPower2 -149
测试脚本测出来的 😂
1 2 3 4 5 6 7 8 9 10 11 12 unsigned floatPower2 (int x) { if (x > 127 ) { return 0x7f800000 ; } else if (x >= -126 && x <= 127 ) { unsigned exp = 127 + x; return exp << 23 ; } else if (x >= -149 && x < -126 ) { return 1 << (x + 149 ); } else { return 0x0 ; } }
注意事项 dlc 检查
成果
总结 做 csapp 的 lab 真的很有意思,做出来跑测试脚本看到分数上涨,成就感满满 🥰🥰。个人觉得最难的应该是 howManyBits 这个题。整个 lab 零零散散花了 3 天才做完,菜菜的,不过确确实实学到东西了,接着加油。