CSAPP: Data Lab
Some Restriction
又复习了一次位级表示
Integer Coding Rules
- Expr
- 整型操作数的值被限制在范围[0, 255]。
- 不能使用全局变量
- 只能使用的一元操作
!
,~
- 只能使用的二元操作
&
,^
,|
,+
,<<
,>>
- 一个表达式不会被限制拥有多个操作符
- Fobidden
- 使用控制语句如
if
,do
,while
,for
,switch
等等 - 定义或使用任何宏
- 在当前文件中定义任何额外的函数
- 调用任何函数
- 使用其他操作
- 使用类型转换
- 使用除
int
之外的任何数据类型,使用arrays
,structs
,unions
- 使用控制语句如
- 假设机器的配置
- 使用2的补码,
int
的表示为32-bit - 执行算术右移
- 如果左移的位数小于0或者大于31则会出现未预测的行为。
- 使用2的补码,
Floating Point Coding Rules
- Forbidden
- 定义或使用任何宏
- 定义任何额外的函数
- 调用任何函数
- 使用任何形式的类型转换
- 使用
arrays
,structs
,unions
Notes
- 使用
dlc
(data tab checker)编译器来检查解决方案的合理性 - 使用
btest
来检查你的函数的正确性 - 使用
BDD checker
来正式地证实你的函数
lab Note
- 64位机器上编译32位程序会出现错误
fatal error: bits/libc-header-start.h: 没有那个文件或目录
,是因为gcc没有安装multilib
库,这个库可以在64位的机器上产生32位的程序sudo apt install gcc-multilib
1. bitXor
通过图中所给的与、非和或门构造的异或逻辑电路,再利用德摩根定律将所有的或门转化为与门可得。
1 | /* bitXor - x^y using only ~ and & |
2. tmin
32位整型数2's complement
的范围为[-2^(n-1), 2^(n-1)-1
, 很容易得出结果
1 | /* |
3. isTmax
摸索了小一段时间,解法很多,-1
可以通过取反+1
来构造。假设x为Tmax
, 对Tmax
取反得到Tmin
,再减1则会发生underflow
会得到Tmax
, 通过逻辑运算的结果与x
本身异或,看结果是否为0
来判断Tmax
。这种情况下-1
是需要排除的, 因为对-1
的2's Complement
取反得到0
,减1之后会得到-1
本身,因此异或结果还是0
,需要排除(利用好!!
,保证与的操作数要么是0
或1
)。排除直接让x+1
判断即可,否则操作数会超出10个。
1 | /* |
4. allOddBits
根据题目给定的操作符数的限制,以及操作数值的限定,即可确定需要利用好0xAA
。同样也花了一些时间去斟酌,很多细节需要把握住,得到最终的答案。
1 | /* |
5. negate
很简单
1 | /* |
6. isAsciiDigit
可以先将操作数限定在0x3X
的范围内,再通过对0x0A
取补得到-10
,取低4位进行加法运算,得到的结果通过符号位,若符号位为1
则为ASCII数字,否则不满足条件。可以通过提取从低位数起第五个bit即0x10
来确定符号位。
1 | /* |
7. conditional
保证x
非0
即1
,再利用0
和1
补码的特点, 构造全1
和全0
, 分别和y
, z
完成与运算来进行排除,这道题还是非常巧妙的。
1 | /* |
8. isLessOrEqual
根据两个参数x
, y
的符号位先分几种情况
- 满足小于等于
- 两数相等
x<0, y>0
, 避免符号位相等时的溢出情况- 符号位相等比较。通过第一个操作数加上第二个操作数的取补的形式, 若结果小于0,则
x<y
- 负负比较, 可能会出现对
Tmin
取补发生下溢出的特殊情况,因此需要排除第二个操作数为Tmin
的情况,恰好该情况要么相等(已经判断过了), 要么就是x>y
(也判断了)。 - 正正比较
- 负负比较, 可能会出现对
- 不满足小于等于,即大于等于(
x>0, y<0
)1
2
3
4
5
6
7
8
9
10
11
12
13/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y) {
int x_sign_bit = ((x>>31)&0x01);
int y_sign_bit = ((y>>31)&0x01);
int Tmin = 1 << 31;
return (!((~x_sign_bit)&y_sign_bit)) & ((!(x^y)) | (x_sign_bit&(~y_sign_bit)) | ((((x+(~y+1))>>31)&0x1)&(!!(y^Tmin))));
}
9. logicalNeg
首先要将想一个办法将0和正数,负数区分开来。因为0和正数的符号位都是0,要区分开来恰好利用到了0的补码还是其本身的特性,正数取反加一后符号位由0变1,可以直接区分开来。再利用算数右移的特性,将0构造成全0,负数和正数构造成全1, 最后+1
得到返回值。
1 | /* |
10. howManyBits
坦白说这一题真的是卡了我好久,即便找到了最高bit位要想返回正确的结果几乎是很繁琐的,但肯定会超出操作符的限制。这道题参考了一下各路佬的思想,可以用二分法来实现。还需要注意负数需要按位取反(排除符号位的影响),正数保持不变即可,可以通过sign_bit
减1
来构造全0
和全1
。可以通过右移16
、8
、4
、2
、1
bit来找到最高的bit 1
,这道题还是非常巧妙的。
判断右移n bit
后是否为0
- 如果为0,说明需要的位在高
n-bit
(左半侧),数则保持不变,故不记录n
,继续进行二分搜索。 - 如果不为0,说明需要的位在低
n-bit
(右半侧), 数向右移动n-bit
继续进行搜索,且记录n
。
最后将所有移位得到的n
都累加起来得到最后的结果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/* howManyBits - return the minimum number of bits required to represent x in
* two's complement
* Examples: howManyBits(12) = 5
* howManyBits(298) = 10
* howManyBits(-5) = 4
* howManyBits(0) = 1
* howManyBits(-1) = 1
* howManyBits(0x80000000) = 32
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int howManyBits(int x) {
int b16, b8, b4, b2, b1, b0;
int sign_bit = (x>>31)&0x1;
int flag = sign_bit+(~1+1); // neg -> all 0, not_neg -> all 1
x = ((~flag)&(~x))|(flag&x); // if neg, reverse.
b16 = (!!(x>>16))<<4;
x >>= b16;
b8 = (!!(x>>8))<<3;
x >>= b8;
b4 = (!!(x>>4))<<2;
x >>= b4;
b2 = (!!(x>>2))<<1;
x >>= b2;
b1 = !!(x>>1);
x >>= b1;
b0 = x;
return b16 + b8 + b4 + b2 + b1 + b0 + 1;
}
11. floatScale2
描述里没有给Infinity
的case,通过测试发现,与Nan
返回值一致合并一起判断。需要判断几种情况: NaN
, Subnormal
, +0
, -0
, Normalize
。Normalize
的情况只需要对Exponent
的最低位增加一个bit
即可。注意Subnormal
情况Fraction
为全1
时左移的结果不需要将Exponent
清零,按测试用例应该是将Subnormal
转化为Normalize
了。强调一下移码的意义为IEEE 754
Normalize表示部分2的指数部分。
$$Bias = 2^{Exponent-1}-1$$
$$Normalize = 1.Fraction \times 2^{Exponent + Bias} (1 \leq Exponent \leq 2^{Exponent} - 2)$$
$$Subnormal = 0.Fraction \times 2^{1-Bias}$$
$$Biased = Unsigned - Bias$$
1 | /* |
12. floatFloat2Int
实现到IEEE754
单精度浮点数到定点整数的转换。题目中首先要考虑溢出的情况,早在最开始实验就假设机器左移的位数小于0或者大于31都会出现未知的行为,因此需要将这两种情况包含进去。由于Normalize
的M
部分是$1.Fraction$因此需要将小数点前面的1
提前加入fraction
部分。实际上fraction
右移23位
可以和$2^{Biased}$的指数部分进行相减$2^{Biased-23}$,进行对应的移位操作完成$2^n$幂次运算,最后得到对应的结果。
1 | /* |
13. floatPower2
给定argument为Biased
,求出其对应无符号IEEE 754
单精度浮点数的表示即可。求出exp的值之后,左移23到IEEE754规格的exponent域中。认真读题后还是蛮简单的。
1 | /* |