csapp(lab1、data lab)

686 阅读3分钟

先去官网下载(csapp lab官网)

image.png

下载之后的目录结构应该是这样的

image.png

readme里面大概写的我们需要修改bits文件。并且make之后看btest通过的例子判断得分

image.png

第一题, 实现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);
}

image.png

第二题,实现tmin

返回最小的二进制补码整数 限定使用 "! ~ & ^ | + << >>"

根据补码的性质 10000...(1后面31个0)就是这个数

所以返回 1 << 31

int tmin(void) {
    return 1 << 31;
}

image.png

第三题,实现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)));
}

image.png

第四题,实现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);
}

image.png

第五题,实现negate

给定x, 返回-x

根据补码的性质
返回 ~x + 1 即可
int negate(int x) {
    /* return ~x + 1 */
    return ~x + 1;
}

image.png

第六题,实现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);
}

image.png

第七题,实现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
}

image.png

第八题,实现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));
}

image.png

第九题,实现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;
}

image.png

第十题,实现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;
}

image.png

第十一题,实现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);
}

image.png

第十二题,实现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;
}

image.png

第十三题,实现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;
}

image.png

最后执行make之后运行btest文件

image.png 发现没有错误。所以这个lab就做完了,总的来说浮点数部分还是很困难,在网上找了很多资料才做出来