CSAPP 配套实验 DataLab
第一次写博客,当作随笔留给自己看吧,如果能帮到读者,是我的荣幸。
这是 CSAPP 的配套实验 DataLab,概括的来说就是在较严格的限制条件下把 15 个函数补充完整。
本人能力没有那么强,很多题目都是借鉴了(抄袭了= =)网上大佬的做法,当然也有些题目看不懂大佬的做法,但是结合了大佬的思想,自己写了出来。
很多函数的下面都附加了自己的理解。
在 ubuntu 12.04 下测试全部通过
每次修改 bits.c 后的测试顺序
./dlc bits.c (检查编码是否符合规则)
make btest (相当于编译检查语法)
btest bits.c (测试bits.c所有函数的正确性)
注:./dlc bits.c,检查编码是否符合规则 这一步非常有必要,因为即使你后面测试函数得到了满分,但是不符合编码规则将视作零分
btest 测试单个函数的方法是 btest -f FuncName,其他更多用法请自行参考说明文件
1
/*
* bitAnd - x&y using only ~ and |
* Example: bitAnd(6, 5) = 4
* Legal ops: ~ |
* Max ops: 8
* Rating: 1
*/
int bitAnd(int x, int y) {
return ~((~x) | (~y));
}
//利用德摩根律
2
/*
* getByte - Extract byte n from word x
* Bytes numbered from 0 (LSB) to 3 (MSB)
* Examples: getByte(0x12345678,1) = 0x56
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 6
* Rating: 2
*/
int getByte(int x, int n) {
return ((x >> (n << 3)) & 0xFF);
}
//将所要取的字节移到最右端然后其余位通过与运算置0
3
/*
* logicalShift - shift x to the right by n, using a logical shift
* Can assume that 0 <= n <= 31
* Examples: logicalShift(0x87654321,4) = 0x08765432
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 20
* Rating: 3
*/
int logicalShift(int x, int n) {
int tmp = 32 + (~n);
return (x >> n) & ((1 << tmp) + (~0) + (1 << tmp));
}
//运用机器的算术右移,然后高位置0和低位保持
//右移n位后原最高位到了第31-n位,31-n表示为31+((~n)+1) = 32+(~n)
//通过与高n位全为0,第32-n位全为1的数实现高位置0和低位保持
//这个数是(1 << ((32+(~n)+1)) - 1
//由于n可能为0,这样左移32位会根据gcc编译规则左移 32 % 32(类型位长) = 0位
//故将该数表示为(1 << (32+(~n)+1)) + (~0) + (1 << (32+(~n)+1))
4
/*
* bitCount - returns count of number of 1's in word
* Examples: bitCount(5) = 2, bitCount(7) = 3
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 40
* Rating: 4
*/
int bitCount(int x) {
//造数
int _tmp1 = 0x55 | (0x55 << 8); //0x00005555
int _tmp2 = 0x33 | (0x33 << 8); //0x00003333
int _tmp3 = 0xf | (0xf <<8); //0x00000f0f
int tmp1 = _tmp1 | (_tmp1 << 16); //0x55555555
int tmp2 = _tmp2 | (_tmp2 << 16); //0x33333333
int tmp3 = _tmp3 | (_tmp3 << 16); //0x0f0f0f0f
int tmp4 = 0xff | (0xff << 16); //0x00ff00ff
int tmp5 = 0xff | (0xff << 8); //0x0000ffff
//求和
int res = 0;
res = (x & tmp1) + ((x >> 1) & tmp1);
res = (res & tmp2) + ((res >> 2) & tmp2);
res = (res & tmp3) + ((res >> 4) & tmp3);
res = (res & tmp4) + ((res >> 8) & tmp4);
res = (res & tmp5) + ((res >> 16) & tmp5);
//返回
return res;
}
//类似递归分治的思想,以统计二进制数x=10中1的个数为例
//方法是将高位移到低位和0x1相与,(x & 0x1) + ((x >> 1) & 0x1)
//造出5个数,0x55555555,0x33333333,0x0f0f0f0f,0x00ff00ff,0x0000ffff
//这五个数写成二进制,分别隔着1,2,4,8,16个0
/*
* bang - Compute !x without using !
* Examples: bang(3) = 0, bang(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int bang(int x) {
return ((~((x | ((~x)+1)) >> 31)) & 0x1);
}
//将一个非零数的补码与其相反数的补码相或,最高位一定是1
//但如果对0进行此操作,最高位还是0
6
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return (0x1 << 31);
}
//最小负整数的二进制补码是1000...0000
7
/*
* fitsBits - return 1 if x can be represented as an
* n-bit, two's complement integer.
* 1 <= n <= 32
* Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int fitsBits(int x, int n) {
return !(((x >> (n+(~0))) + 1) >> 1);
}
//若n位能表示这个数
//正数的1只能出现在低n-1位,其余位全为0
//负数的0只能出现在低n-1位,其余位全为1
//故将该数右移n-1位后所得结果,正数全为0,负数全为1
//此时再+1后右移1位,可以得到全0
//若n位不能表示这个数,则一定不会有以上结论
//n-1表示为n+(-1),即n+(~0)
8
/*
* divpwr2 - Compute x/(2^n), for 0 <= n <= 30
* Round toward zero
* Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int divpwr2(int x, int n) {
return ( x + (((x >> 31) & 0x1) << n) + (~0) + (!((x >> 31) & 0x1)) ) >> n;
}
//本题是对于标准除法/的实现,全正取下整,有负取上整
//而通过移位实现除以2的幂,都是取下整
//故对于负数要加一个偏移量(1 << n) - 1 (证明在深入理解计算机系统第9版P73)
//因为是负数才要加偏移量,所以式子中的1刚好可以用符号位((x >> 31) & 0x1)表示
//若x > 0,(((x >> 31) & 0x1) << n)的结果为0,会导致多减去了1即(~0)
//巧妙多加一个符号位的逻辑非结果即(!((x >> 31) & 0x1)),负数为0,整数为1,恰好弥补
9
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ((~x) + 1);
}
//对一个数的补码进行按位取反加1就能得到其相反数的补码
10
/*
* isPositive - return 1 if x > 0, return 0 otherwise
* Example: isPositive(-1) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 8
* Rating: 3
*/
int isPositive(int x) {
return !( ((x >> 31) & 0x1) | (!(x << 1)) );
}
//正数和零的符号位为0,负数为1
//左移1位后用!,正数的结果一定为0,负数的结果可0可1,零的结果一定是1
//将以上两个结果相或,正数的结果为0,负数一定为1,零的结果一定是1
11
/*
* 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 xSign = (x >> 31) & 0x1;
int ySign = (y >> 31) & 0x1;
int signDiff = xSign & (!ySign);
int signSame = !(((y + (~x+1)) >> 31) & 0x1) & !(xSign ^ ySign);
return signDiff | signSame;
}
//作差法判断大小
//首先要判断符号位,因为可能存在溢出
//正数符号位为0,负数符号位为1,列出x,y的真值表
//x y z
//0 0 符号相同要进行下一步作差
//0 1 0(表示x > y)
//1 0 1(表示x < y)
//1 1 符号相同要进行下一步作差
//可以看到若用一个表达式(xSign & !(ySign))可以实现只用第三种情况为1,就可以完成符号位的判断
//接下来还要看其他三种情况,通过与上!(xSign ^ ySign)保证两者同号,因为第二种不同号的情况在该表达式下结果为0,与0就置0
//前面取y-x的符号位,为0说明,y >= x,否则y < x,再取逻辑非配合与操作
12
/*
* ilog2 - return floor(log base 2 of x), where x > 0
* Example: ilog2(16) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int ilog2(int x) {
int res = 0;
res = res + ((!!(x>>(16 + res)))<<4);
res = res + ((!!(x>>(8 + res)))<<3);
res = res + ((!!(x>>(4 + res)))<<2);
res = res + ((!!(x>>(2 + res)))<<1);
res = res + ((!!(x>>(1 + res)))<<0);
return res;
}
13
/*
* float_neg - Return bit-level equivalent of expression -f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representations of
* single-precision floating point values.
* When argument is NaN, return argument.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 10
* Rating: 2
*/
unsigned float_neg(unsigned uf) {
unsigned exp = (uf >> 23) & 0xFF;
unsigned frac = uf & 0x7FFFFF;
unsigned res = uf ^ 0x80000000;
if(exp == 0xFF && frac) {
res = uf;
}
return res;
}
//取exp和frac判断是否为NaN
//异或改变符号位
14
/*
* float_i2f - Return bit-level equivalent of expression (float) x
* Result is returned as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point values.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_i2f(int x) {
unsigned ans;
int xSignx = x & (1 << 31);
int res = 31;
int ss = 0;
int ff = 0;
int tmp;
if(x == 0) ans = 0;
else{
if(xSignx) x = (~x) + 1;
while(!((1 << res) & x)) {
res--;
}
x = x ^ (1 << res);
if(res < 23) x = x << (23 - res);
else {
tmp = res - 24;
if(tmp >= 0) ss = (x >> tmp) & 1,ff = ((1 << tmp) - 1) & x;
x = (x >> (res-23));
}
x = x | ((res+127) << 23);
if(ff == 0) {
ss = (ss & x);
}
x = x + ss;
x = x | xSignx;
ans = x;
}
return ans;
}
15
/*
* float_twice - Return bit-level equivalent of expression 2*f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representation of
* single-precision floating point values.
* When argument is NaN, return argument
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_twice(unsigned uf) {
unsigned tmp = uf;
unsigned exp = (tmp >> 23) & 0xFF;
unsigned frac = tmp & 0x007fffff;
if(exp == 0x0) { //非规格化数
tmp = (tmp & 0x80000000) | (frac << 1);
}
else if(exp != 0xFF) { //规格化数
tmp += (1 << 23);
if(((tmp >> 23) & 0xFF) == 0xFF) {
tmp = tmp >> 23 << 23;
}
}
return tmp;
}