先去官网下载(csapp lab官网)
下载之后的目录结构应该是这样的
readme里面大概写的我们需要修改bits文件。并且make之后看btest通过的例子判断得分
第一题, 实现bitXor
使用 ~ 和 &实现 ^
x ^ y = (x | y) & (~x | ~y)
a | b = ~(~a & ~b)
所以 x ^ y = ~(~x & ~y) & ~(x & y)
int bitXor(int x, int y) {
return ~(~x & ~y) & ~(x & y);
}
第二题,实现tmin
返回最小的二进制补码整数 限定使用 "! ~ & ^ | + << >>"
根据补码的性质 10000...(1后面31个0)就是这个数
所以返回 1 << 31
int tmin(void) {
return 1 << 31;
}
第三题,实现isTmax
如果x等于tmax(011111...)返回1,其他的返回0
tmax有一个性质
tmax (~(tmax + 1)) = 000000...
但是0xffffffff也满足这个,所以需要排除0xffffffff
所以返回 !((x ^ (~(x + 1))) | (!(~x)))
第一部分只有tmax和0xffffffff为0
第二部分只有tmax为0
int isTmax(int x) {
/* if x is tmax then tmax == ~(tmax + 1) */
/* but x != 0xffffffff */
/* if x == tmax , x ^ (~(x + 1)) == 0x00000... and !(~x) == false, return true(1) */
/* if x == 0xffffffff, x ^(~(x + 1)) == 0x00000... but !(~x) == true , return false(0)*/
/* if x != tmax and x != 0xffffffff, x ^ (~(x + 1)) != 0, return false(0)*/
return !((x ^ (~(x + 1))) | (!(~x)));
}
第四题,实现addOddBits
如果一个数的偶数位都为1则返回1,其他的返回0
就是判断一个数与0xAAAAAAAA是否相等。
即返回!(x ^ 0xAAAAAAAA)
不过上面有限制,不能直接使用超过8位的数
int allOddBits(int x) {
/* if x == 0xAAAAAAAA return 1 */
/* if x ^ 0xAAAAAAAA == 0 return 1 else return 1 */
/* so return !(x ^ 0xAAAAAAAA) */
int mask = 0xAA | 0xAA << 8;
mask = mask << 16 | mask;
x = x & mask;
return !(mask ^ x);
}
第五题,实现negate
给定x, 返回-x
根据补码的性质
返回 ~x + 1 即可
int negate(int x) {
/* return ~x + 1 */
return ~x + 1;
}
第六题,实现isAsciiDigit
如果0x30 <= x <= 0x39则返回1,其他的返回0
让我们看4位的补码 即(-8~7), 选出2-4
我们拿一个数0011(符号位为0,整数部分为4的反码),加上比4大的数会发生溢出,符号位会为1。再看另外一个数为1101(2的反码),加上比2小的数再加1不会发生溢出,符号位会为1
所以我们就有以下代码来推导
int isAsciiDigit(int x) {
/* if upperBound add a number which biger 0x39 upperBound change negate */
/* if lowerBound add a number which lower 0x30 lowerBound change negate */
int sign = 0x1 << 31;
int upperBound = ~(sign | 0x39);
int lowerBound = ~0x30;
upperBound = sign & (upperBound + x) >> 31;
lowerBound = sign & (lowerBound + 1 + x) >> 31;
return !(upperBound | lowerBound);
}
第七题,实现coditional
给定x,y,z。实现 x? y: z;
先将x转换为0或者1。x = !!x; 如果x为0则x = !!x 后x为0,x为非0的话 x = !!x 后 x 为 1 再将x 转换为 ~x + 1。如果x一开始为0的话,x为0x00000000, 否则x 为0xffffffff 最后返回 (x & y) | (~x & z)即为所求
int conditional(int x, int y, int z) {
x = !!x; // if x == 0, then x = 0, else x = 1
x = ~x + 1; // if x == 0, then x = 0x00000000, else x = 0xffffffff
return (x & y) | (~x & z); // if x == 0, then return z, else return y
}
第八题,实现isLessOrEqual
如果x <= y 返回1,其他情况返回0 x <= y 则 y - x >= 0。但是如果x和y正负性相反的话,可能会溢出 这里就分两种情况判断。y - x >= 0或者y >= 0 && x < 0返回1,其他返回0 这里的代码就是判断y - x的符号和x y是否正负性相反并且x为负
int isLessOrEqual(int x, int y) {
int negx = ~x + 1; // -x
int addx = negx + y;// y - x
int sign = addx >> 31 & 1; // (y - x)' sign, if x less or equal y, then y - x >= 0
int leftBit = 1 << 31; // 0b1000000...
int signx = x & leftBit; // x' sign
int signy = y & leftBit; // y' sign
int bitXor = signx ^ signy; // if x and y both positive or negate then bitXor is 0 else bitXor is not zero
bitXor = (bitXor >> 31) & 1; // if x and y both positive or negate then bitXor is 0 else bitXor is 1
return ((!bitXor) & (!sign)) | (bitXor & (signx >> 31));
}
第九题,实现logicalNeg
如果是0,返回1;如果是其他,返回0 0有一个特点 0 的补码就是它自己,所以 0 | (~0 + 1) >> 31为 0。其他数都会得到0xffffffff 所以 返回((x | (~x + 1)) >> 31) + 1
int logicalNeg(int x) {
/* if x equal 0, then (x | (~x + 1)) >> 31 if 0x00000000, return 1*/
/* else return 0xffffffff + 1 equal 0 */
return ((x | (~x + 1)) >> 31) + 1;
}
第十题,实现howManyBits
返回一个数表示它的二进制补码需要多少位。 例如 -5返回 4, 12返回5 所以我们只需要异或相邻的数x^=(x<<1),找出为1的最高位在哪一位就可以了。
int howManyBits(int x) {
int n = 0;
x ^= (x<<1);
n += ((!!(x & ((~0) << (n + 16)))) << 4);
n += ((!!(x & ((~0) << (n + 8)))) << 3);
n += ((!!(x & ((~0) << (n + 4)))) << 2);
n += ((!!(x & ((~0) << (n + 2)))) << 1);
n += (!!(x & ((~0) << (n + 1))));
return n + 1;
}
第十一题,实现floatScale2
返回浮点数0.5*f 考虑NaN和INF的特殊情况,即指数exp==255,if((uf&0x7fffffff) >= 0x7f800000),返回参数。考虑为+0/-0的情况,if(!(uf&0x7fffffff)),返回0。
规格化小数0.5若结果仍为规格化小数,这时候只需要指数-1,其他部分不变即可。(uf&0x807fffff)|(--exp_)<<23。规格化小数0.5,可能结果变为非规格化。即exp==0或exp==1时的情况,这时候只处理小数部分,因为非规格化小数其指数部分为0,右移一位表示*0.5。
unsigned floatScale2(unsigned uf) {
int exp = (uf & 0x7f800000) >> 23;
int sign = uf & (1 << 31);
if(exp == 0)
return uf << 1 | sign;
if(exp == 255)
return uf;
exp++;
if(exp == 255)
return 0x7f800000 | sign;
return (exp << 23) | (uf & 0x807fffff);
}
第十二题,实现floatFloat2Int
先将浮点数分成三段,符号部分s_ = uf>>31,指数大小exp_ = ((uf&0x7f800000)>>23)-127,获取小数部分,并补上浮点数缺省的1,frac_ = (uf&0x007fffff)|0x00800000。
处理特殊情况全为0是返回0,若指数大于31,整数无法表示溢出返回0x80000000。若指数小于0,该数0<x<1返回0。
若指数部分大于23则将小数部分向左移动frac_ <<= (exp_ - 23) ,exp_代表指数大小。
若指数部分小于23则将小数部分向右移动frac_ >>= (23 - exp_) ,exp_代表指数大小。
考虑最后符号,正数转换为负数不会产生溢出。若frac_为正数,则根据s_调整正负输出即可。
若frac_为负数,唯一正确情况为0x80000000。
int floatFloat2Int(unsigned uf) {
int s_ = uf >> 31;
int exp_ = ((uf & 0x7f800000) >> 23) - 127;
int frac_ = (uf & 0x007fffff) | 0x00800000;
if(!(uf & 0x7fffffff))
return 0;
if(exp_ > 31)
return 0x80000000;
if(exp_ < 0)
return 0;
if(exp_ > 23)
frac_ <<= (exp_ - 23);
else
frac_ >>= (23-exp_);
if(!((frac_>>31)^s_))
return frac_;
else if(frac_>>31)
return 0x80000000;
else
return ~frac_+1;
}
第十三题,实现floatPower2
计算浮点数2.0^x。若结果太小返回0,太大返回 +INF
unsigned floatPower2(int x) {
int INF = 0xff<<23;
int exp = x + 127;
if(exp <= 0)
return 0;
if(exp >= 255)
return INF;
return exp << 23;
}
最后执行make之后运行btest文件
发现没有错误。所以这个lab就做完了,总的来说浮点数部分还是很困难,在网上找了很多资料才做出来