CSAPP - Data Lab 详解
Data Lab [Updated 12/16/19] (README, Writeup, Release Notes, Self-Study Handout)
Students implement simple logical, two’s complement, and floating point functions, but using a highly restricted subset of C. For example, they might be asked to compute the absolute value of a number using only bit-level operations and straightline code. This lab helps students understand the bit-level representations of C data types and the bit-level behavior of the operations on data.
实验作业网址:http://csapp.cs.cmu.edu/3e/labs.html
前言
本篇博客将会剖析 CSAPP - DataLab 各个习题的解题过程,加深对 int、unsigned、float 这几种数据类型的计算机表示方式的理解。
DataLab 中包含下表所示的 12 个习题,其中 9 个和整数有关,3个和单精度浮点数有关。
函数名 | 功能描述 | 分数 | 操作符 |
---|---|---|---|
bitXor(x, y) | 使用 & 和 ~ 实现异或操作 | 1 | 14 |
tmin() | 补码的最小值 | 1 | 14 |
isTmax(x) | x 是否为补码的最大值 | 1 | 10 |
allOddBits(x) | x 的奇数位是否全为 1 | 2 | 12 |
negate(x) | 不使用 - 计算 x 的相反数 | 2 | 5 |
isAsciDigit(x) | x 是否在 [0x30, 0x39] 区间内 | 3 | 15 |
conditional | 实现条件运算符,x ? y : z | 3 | 16 |
isLessOrEqual(x, y) | x 是否小于等于 y | 3 | 24 |
logicalNeg(x) | 不使用 ! 计算逻辑非 | 4 | 12 |
howManyBits(x) | 表示 x 的最少补码位数 | 4 | 90 |
floatScale2(uf) | 计算无符号数 uf 所表示的浮点数的 2 倍值 | 4 | 30 |
floatFloat2Int(uf) | 将无符号数 uf 所表示的浮点数转为整数 | 4 | 30 |
floatPower2(x) | 计算 | 4 | 30 |
bitXor
思路:先使用&
、|
、~
进行表示,然后利用DeMorgan律去替换掉不能使用的位运算。
先画出真值表:
1 | x y x^y |
思路1:看什么情况得到1
“x为0,y为1“或”x为1,y为0”即可:x^y = (x&~y) | (~x&y)
根据DeMorgan律:x|y = ~(~x & ~y)
即 x^y = (x&~y) | (~x&y) = ~(~(x&~y) & ~(~x&y))
思路2:排除掉得到0的情况
只要x、y不同时为0,或同时为1就行:x^y = ~(~x&~y) & ~(x&y)
思路3:^和|像,再用~(x&y)加掩码
x^y = x|y & ~(x&y)
利用DeMorgan律,得x^y = ~(~x&~y) & ~(x&y)
1 | /* |
Tmin
Tmin形如1000000000,可通过移位表示:
1 | /* |
isTmax
Tmax形如0111111…111,特点:~(Tmax+1)=Tmax,可通过这个性质判断
但是Umax = 1111111…111也满足 ~(Umax+1)=Umax,需要排除
二者不同在于Tmax+1不为0,而Umax+1等于0,”是否等于0“可以通过!!
转化为布尔类型,否则直接&的话不对喔。
💡通过
!(x ^ y)
判断是否相等
通过!
或!!
将结果转化为bool类型
1 | /* |
addOddBits
首先产生1010…1010 即 0xAAAAAAAA 形式的掩码,对x的奇数位进行提取,然后判断提取结果是否为0xAAAAAAAA。
1 | /* |
negate
💡 求负:逐位取反再加1
1 | /* |
isAsciiDigit
0x30 位模式为 00…00 0011 0000
0x39 位模式为 00…00 0011 1001
可以分成两步来判断:
- 前24位为 00…00 0011
- 后8位范围为 0000-1001,判断方法为减去1010后,判断是否为负数
1 | /* |
conditional
结果一定形如:return (expr & y) | (~expr & z);
当x=0时,expr = 0, ~expr = 11…11
当x≠0时,expr = 11…11, ~exp = 0
1 | /* |
isLessOrEqual
直接想法是做减法,判断差的符号。
但注意,只有同号时做+/-才不用考虑溢出,其他情况都需要单独判断。
1 | isLessOrEqual 等价于 x <= y ? 1 : 0; |
代码:
1 | /* |
💡 优先级: ! ~ + << & |
logicalNeg
关键:找出0和非0数的关系,非0数有正负,x|(-x)的符号位一定为1
1 | /* |
howManyBits
- 0 只需要1位表示
- 负数,对于-1(111, 11111, 111111)都是只需要1位表示
- 对于更一般的 1110 0011, 需要1位表示符号,然后将最高位0的位数命名位high_bit,则需要high_bit + 1位表示(可以通过取反,看最高位1确定high_bit)
也可以这样理解:1位符号位+数值的无符号数表示,就像:bin(-7)='-0b111
- 对于更一般的 1110 0011, 需要1位表示符号,然后将最高位0的位数命名位high_bit,则需要high_bit + 1位表示(可以通过取反,看最高位1确定high_bit)
- 正数,需要1位符号位+数值表示
现在难点就变成如何求high_bit,即求最高位的1:
0010 0000 0000 0000 | 0000 0000 0000 0000
0000 0000 0000 0000 | 0001 0000 0000 0000
上面给出2个例子,首先看高16位是否有1
若有,右移动16位,继续去看1在哪(在高16位中同理去求)
若无,则意味着高16位全为0,不需要右移(在低16位中同理)
1 | /* howManyBits - return the minimum number of bits required to represent x in |
floatScale2
- 对于InF/NaN,直接输出argument
- 对于非规范数,直接将frac左移1位——溢出到exp阶码域没关系,结果依然是对的
- 对于规范数,由于E位于指数位置,E直接增一就行,exp = E + bias也是增一。由于EXP取值范围为[1, 254], 增加到255也没关系,INF/NaN都行。
💡对于Denormalized Values,frac直接左移1位,移除到阶码域结果也是正确的。
尾数域最高位权重为1/2, 移除到阶码域变成规范数后,M=1+frac,这时E由于解释方式的差异值不变,而M多的这个1恰好是1/2的2倍。
1 | /* |
floatFloat2Int
先排除denormalized number和speical number,对于规范数:
先通过E = exp - 127 计算阶码
再通过 |0x800000
(5个0)把隐含的1加上,然后根据E进行移位
最后根据符号位,看是否需要加负号
1 | /* |
floatPower2
1 | /* |
参考资料
- 【【深入理解计算机系统 实验1 CSAPP】datalab + 环境搭建 data lab】 https://www.bilibili.com/video/BV183411k7VM/?share_source=copy_web&vd_source=1e8c177289cfed3be80e766714c3f11f