CSAPP大实验期末作业
在给出的readme中,它提供了一些工具和方法。例如
这是dlc 工具,用来检查bits.c文件中各个函数是否有违法的符号。
还可使用btest来测试bits.c 的正确通过样例数,每次测试都需要使用make编译。
可以单独测试某一个函数是否正确
或者全部的函数:
endif
bitAnd - x&y using only ~ and |
- Example: bitAnd(6, 5) = 4
- Legal ops: ~ |
- Max ops: 8
- Rating: 1
int bitAnd(int x, int y) {
int ret = ~(~x | ~y);
return ret;
}
getByte
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) {
int tmp = ((x << ((4 + (~n)) << 3)) >> 24);
return tmp&0x000000ff;
}
思路是得到Byte后,先左移使有效位到最左边,再右移24位到最右边,这样就使得前面置0.将其移动到最左边的位数可通过n计算,但因为不可以使用减号,通过使(3 - n = 3 + ~n + 1) << 3的到左移的位数。
logicalShift
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 ret = (x >> n) & (~(((1<<31)>>n)<<1));
return ret;
}
思路是逻辑右移补零,当负数时,消去前面的1,特殊情况时当n = 0的时候需要特别注意。
bitCount
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) { // 用二分法,从小到大依次获取2、4、8、16、32个01位中1的个数。
int bitcount;
int tmp1 = (0x55)|(0x55<<8);
int mask1 = (tmp1)|(tmp1<<16); //得到01010101……01010101
int tmp2 = (0x33)|(0x33<<8);
int mask2 = (tmp2)|(tmp2<<16); //得到00110011……00110011
int tmp3 = (0x0f)|(0x0f<<8);
int mask3 = (tmp3)|(tmp3<<16); //得到 00001111……00001111
int mask4 = (0xff)|(0xff<<16); //得到 0000 0000 1111 1111 0000 0000 1111 1111
int mask5 = (0xff)|(0xff<<8); //得到 0000 0000 0000 0000 1111 1111 1111 1111
bitcount = (x & mask1) + ((x>>1) & mask1); //分别计算每组2位中,低位的1的个数;再移位求每组2位中,高位的1的个数,下面类似
bitcount = (bitcount & mask2) + ((bitcount >> 2) & mask2);
bitcount = (bitcount + (bitcount >> 4)) & mask3;
bitcount = (bitcount + (bitcount >> 8)) & mask4;
bitcount = (bitcount + (bitcount >> 16)) & mask5;
return bitcount;
}
bang
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 + 1) | x) >> 31) + 1;
}
思路是只有0取反+1后会变成0,然后或上x即0之后,右移31位还是0,+1后就得到了1. 而其它的任何数,由于一个数与其负数相或,结果一定是负数,因此右移31位后,一定是-1,+1后即变成了0.
tmin
tmin - return minimum two’s complement integer
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 4
- Rating: 1
int tmin(void) {
return (1<<31);
}
fitsbits
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) {
int shiftNumber = 32 + ~n + 1;
int tmp = x << shiftNumber >> shiftNumber;
return !(tmp ^ x);
}
思路是经过 x << (32 - n) >> (32 - n)之后,如果能用n位表示,则一定与原来x相同。判断一个数是否和另一个数相同用异或即可,相同则为0。最后加!,如果相同,则返回1,表示能用n位表示x。
divpwr2
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) {
int signMask = x >> 31;
return (x + (signMask & ((1 << n) + ~0))) >> n;
}
因为是有符号数(补码)的移位操作,所以,需要对符号位进行一下判断,思路是分类讨论一下x的正负情况。
negate
negate - return -x
- Example: negate(1) = -1.
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 5
- Rating: 2
int negate(int x) {
return (~x) + 1;
}
这题比较水。。。可以按定义来
isPositive
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 | (x >> 31));
}
思路是取符号位,同时判断是否为0,还要小心正偶数,因为是判断最低位
isLessOrEqual
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 xl = (x >> 31) & 1;
int yl = (y >> 31) & 1;
int zl = ((y + ~x + 1) >> 31) & 1;
return ((xl ^ yl) & xl) | (!(xl ^ yl) & !zl);
}
这个也需要分类讨论一下。小于或等于的话,不可以简单地调用isPositive来实现,因为相减时会溢出,取符号位,判断是否是x<0且y>=0,如果是则直接返回1;否则如果同号,则判断y-x的符号,如果为0,说明x小于等于y
ilog2
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 bitsNumber;
bitsNumber = (!!(x >> 16)) << 4;
bitsNumber = bitsNumber + ((!!(x >> (bitsNumber+8))) << 3);
bitsNumber = bitsNumber + ((!!(x >> (bitsNumber+4))) << 2);
bitsNumber = bitsNumber + ((!!(x >> (bitsNumber+2))) << 1);
bitsNumber = bitsNumber + (!!(x >> (bitsNumber+1)));
return bitsNumber;
}
思路是类似用二分搜索最高位为1的位置,比如说找到为第k位,那么结果就是k
float_neg
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 result;
unsigned tmp;
result = uf ^ (0x80 << 24);
tmp = uf & 0x7FFFFFFF;
if( tmp > 0x7F800000)
return uf;
return result;
}
思路是在最高位置为0情况下,如果大于0x7F800000,一定是NaN,直接返回。如果不大于符号为取反,返回
float_i2f
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) {
int sign=x>>31&1;//获取符号位(1位)
int i;
int exponent; //指数域(8位)
int fraction; //小数域
int delta;//偏差(用于舍入)
int fraction_mask;//小数域的掩码(23位)
if(x==0)//如果为0就直接返回
return x;
else if(x==0x80000000)//如果为TMin,解释为-(2^31),对应float就是(1)x1.0x2^31,所以S=1,M=1,E=31,指数域=31+127=158
exponent=158;
else{
if (sign)//通过前面的操作已经确定了符号,先把int的绝对值获取,之后利用浮点数的计算公式即可计算出float的值,现在要获取绝对值存入内存中
x = -x;
i = 30;//最高位是符号位,次高位是有效数字的起始位
while ( !(x >> i) )//从左往右查找有效数字第一个不为零的位,对应的位置就是最终的i(这里的位置从0开始标号)
i--;
//printf("%x %d\n",x,i);
exponent = i + 127;//数值的最高位已经找到是第i位(有效数据共有i+1位),又因为int类型不可能是非规格数据的范围(为0的情况在前面已排除),所以小数域就是d第i为后面的位向量(小数域一共有i个位),故阶码E=i(小数部分x2的E次方),指数域等于i+127;
x = x << (31 - i);//清除有效数据前面的所有0,包括符号位,得到有效数据开头的数据
fraction_mask = 0x7fffff;//设置23位的小数域掩码
fraction = fraction_mask & (x >> 8);//虽然按照浮点数格式,最前面的9个位不加入小数位,按道理应该右移9位,
//但是由于int类型的参数不可能是非规格数,所以最前面的一个有效数据也被舍弃(默认M=1+f),
//当向右移动8位,舍弃了有效数据的低8位,再和掩码处理以后,一共舍弃了9位
//除了低八位还包括有效数据的最高位,类比二进制小数中小数点左边的那一位数字,在float存储的时候,小数点左边数字不存入内存
//者9个位用来存储符号位+指数域
x = x & 0xff;//由于右移8位,舍弃了有效数据第八位,现在获取低八位用于舍入操作
delta = x > 128 || ((x == 128) && (fraction & 1));//如果低八位超过八位二进制能表示的无符号数的一半,
//要在小数域+1,普通的四舍五入思想
//如果低八位刚好等于八位二进制能表示的无符号数的一半,而且小数域目前最后一位是1,
//根据向偶数舍入的模式,也要在小数域+1,向上舍入的思想,
//如果低八位刚好等于八位二进制能表示的无符号数的一半,如果小数域目前最后一位是0,则向下舍入,不加1
//如果低八位刚好小于八位二进制能表示的无符号数的一半,直接丢弃,不加1,普通的四舍五入思想
fraction += delta;//进行舍入
if(fraction >> 23) {//如果舍入过后,小数域多余23位,则只取低23位,高位舍弃,但是阶码E要加1,所以指数域也就要加1
fraction &= fraction_mask;
exponent += 1;
}
}
return (sign<<31)|(exponent<<23)|fraction;//符号位最高位(31),指数域(30--23),小数域(22-0)
}
float_twice
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) {
if( (uf & 0x7F800000) == 0 )
uf = ((uf & 0x007FFFFF) << 1) | (0x80000000 & uf);
else if (( uf & 0x7F800000) != 0x7F800000)
uf = uf + 0x800000;
return uf;
}
如果阶码为0,那么就是非规格数,直接将尾数左移1位到阶码域上,其他不变即可。例如 0 00000000 1000…001 变成 0 00000001 000…0010。这样可以做的原因正是由于非规格化数的指数E = 1 - bias,而不是-bias。这样使得可以平滑地从非规格数过度到规格化数。
如果阶码不为0且不是255,那么直接阶码加1即可。 如果阶码为255,那么是NaN,∞,-∞,直接返回即可。