CSAPP里的实验环境Linux+gcc,首先在虚拟机里安装了Ubuntu,然后分别执行这些命令:
apt-get update 更新apt软件源
sudo apt-get install build-essential 安装C/C++编译环境
sudo apt-get install gcc-multilib 补充gcc完整环境
sudo apt-get install gdb 安装gdb
实验环境就搭建好了。下面是关于第一次实验DataLab的笔记。
- bitxor 实现按位异或。
首先获得x和y都是0或1的位,将这些为置0,其他位置1就是异或了。int bitXor(int x, int y) { int both_one = x & y; // 都是1的位 int both_zero = (~x) & (~y); // 都是0的位 return (~both_one) & (~both_zero); // 其他位置1 }
- tmin 返回用补码表示的最小int值。
补码表示的最小int即10…0。int tmin(void) { return (1 << 31); }
- isTmax 当x是int的最大值时返回1。
利用~Tmax == Tmax + 1
和a ^ a == 0
int isTmax(int x) { // 但是-1也满足此式,因此单独检查该情况 int minus_one = !~x; // 只有-1会让minus_one取1,其他数都使其取0 return !(((~x) ^ (x + 1)) | minus_one); }
- allOddBits 当所有奇数位都是1时返回1,位序号从右向左从0开始。
先把所有位都取反,然后置所有偶数位为0,如果x是满足条件的数的话此时应该等于0。int allOddBits(int x) { int mask = 0x55; // 0101 0101 // 置所有偶数位为1 int temp = x | mask; mask <<= 8; temp |= mask; mask <<= 8; temp |= mask; mask <<= 8; temp |= mask; // 取反 return !~temp; }
- negate 返回-x。
-x在补码的表示就是取反+1。int negate(int x) { return ~x + 1; }
- isAsciiDigit 当
0x30 <= x <= 0x39
(即ASCII码的0~9)时返回1。
满足两个条件:0x30 - x <= 0
和0x39 - x >= 0
即可,由于小于等于0时符号位不固定,因此将其转化为判断小于。int isAsciiDigit(int x) { // 0x30 - x <= 0 -> 0x2F - x < 0 // 0x39 - x >= 0 int minus_x = ~x + 1; int condition1 = !!((0x2F + minus_x) >> 31); int condition2 = !((0x39 + minus_x) >> 31); return (condition1 & condition2); }
- conditional 实现
x ? y : z
int conditional(int x, int y, int z) { return ((!!x << 31 >> 31) & y) | ((!x << 31 >> 31) & z); }
- isLessOrEqual 当x<=y时返回1。
当x, y同号时满足y - x >= 0
即可,当异号时满足x为负,y为正即可。
第一次做的时候只考虑了int isLessOrEqual(int x, int y) { // 异号 int xIsNeg = x >> 31; int yIsNeg = y >> 31; int dif_sign = xIsNeg ^ yIsNeg; // 异号时为1 // 同号 int minus_x = ~x + 1; return ((dif_sign & xIsNeg & !yIsNeg) | (!dif_sign & !(y + minus_x >> 31))) & 1; }
y - x >= 0
的情况,然而若同号,相减可能会溢出。 - logicalNeg 实现
!
运算符。
除了0以外的任何int值做!运算结果都是1,因此只要找出一种只有0是特殊结果的结果即可。
x | ~x
的符号位总是1,除非x=0。int logicalNeg(int x) { int minus_x = ~x + 1; return ((x | minus_x) >> 31 ) + 1; }
- howManyBits 返回表示x所需的最少位数。
这题不会做,而且我也不知道题目实例中howManyBits(-1) = 1
是为什么,不应该需要2位吗? - floatScale2 实现单精度浮点数*2的操作,下面的几道浮点数题都是对unsigned操作的。
exp表示8位阶码,frac表示23位尾数。
当uf是NaN或者无穷大时原样返回即可,当uf是非规格化数时给尾数×2即可,当uf是规格化数时exp+1即可,若此时exp=255,则返回无穷大。unsigned floatScale2(unsigned uf) { int exp = (uf & 0x7F800000) >> 23; int sign = uf & 0x80000000; // 无穷大或NaN if (exp == 255) return uf; // 非规格化数 else if (exp == 0) return (uf << 1) | sign; // 规格化数 exp++; if (exp == 255) return 0x7F800000 | sign; return (exp << 23) | (uf & 0x807FFFFF); }
- floatFloat2Int 转int。
阶码是加过偏移常数127的,尾数有一个默认的1。当指数>31或<0时都会超出表示范围。若转换后符号位改变则说明发生了溢出,只要取其相反数即可。int floatFloat2Int(unsigned uf) { int exp = (uf & 0x7F800000) >> 23; int sign = uf >> 31; int frac = uf & 0x007FFFFF; int E = exp - 127; // M = frac + 1 int M = frac | 0x00800000; if (E > 31) return 0x80000000; else if (E < 0) return 0; if (E > 23) M <<= E - 23; else M >>= 23 - E; // 没溢出 if (!(M >> 31) ^ sign) return M; else return ~M + 1; }
- floatPower2 实现2的幂操作。
unsigned floatPower2(int x) { int exp = x + 127; if (exp >= 255) return 0x7F800000; else if (exp <= 0) return 0; else return exp << 23; }