CSAPP DataLab

1. DataLab

DirectoryLayout:

.
├── bits.c
├── bits.h
├── bits.p.c
├── btest
├── btest.c
├── btest.h
├── cat
├── decl.c
├── dlc
├── Driverhdrs.pm
├── Driverlib.pm
├── driver.pl
├── echo
├── fshow.c
├── ishow.c
├── Makefile
├── README
└── tests.c

Problem 1

Code:

/* 
 * 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));
}

Explaination:

x & y 即 ~(~x | ~y)

Problem 2

Code:

/* 
 * 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 shift_bits = n << 3;
        int mask = 0xff << shift_bits;
        return ((x & mask) >> shift_bits) & 0xff;
}

Explaination:
利用掩码获取指定字节的值,注意C的右移运算符>>是算术右移,若x为负数,在成功获取到对应字节值后仍需要与0xff进行一次按位与操作,以消除高位的1.

Problem 3

Code:

/* 
 * 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 sign_num = (0x1 << 31) & x;
        return (x >> n) ^ ((sign_num >> n) << 1);
}

Explaination:
要实现逻辑右移,首先得获取符号位,通过(0x1 << 31) & x获取到x的符号位后,对x进行算术右移(x >> n),并将结果与高n位填充为符号位(((sign_num >> n) << 1))进行按位与运算即可得到逻辑右移的结果。

Problem 4

Code:

/*
 * 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 mask1 = (0x55 << 24) | (0x55 << 16) | (0x55 << 8) | 0x55;
        // int mask2 = (0x33 << 24) | (0x33 << 16) | (0x33 << 8) | 0x33;
        // int mask3 = (0x0F << 24) | (0x0F << 16) | (0x0F << 8) | 0x0F;
        // int mask4 = (0xFF << 16) | 0xFF;
        // int mask5 = (0xFF << 8 ) | 0xFF;
        int _mask1 = (0x55 << 8) | 0x55;
        int _mask2 = (0x33 << 8) | 0x33;
        int _mask3 = (0x0F << 8) | 0x0F;
        int mask1 = (_mask1 << 16) | _mask1;
        int mask2 = (_mask2 << 16) | _mask2;
        int mask3 = (_mask3 << 16) | _mask3;
        int mask4 = (0xFF << 16) | 0xFF;
        int mask5 = (0xFF << 8 ) | 0xFF;
        
        x = (x & mask1) + ((x >> 1 ) & mask1);
        x = (x & mask2) + ((x >> 2 ) & mask2);
        x = (x & mask3) + ((x >> 4 ) & mask3);
        x = (x & mask4) + ((x >> 8 ) & mask4);
        x = (x & mask5) + ((x >> 16) & mask5);

        return x;
}

Explaination:

十六进制二进制
0x5555555501010101010101010101010101010101
0x3333333300110011001100110011001100110011
0x0F0F0F0F00001111000011110000111100001111
0x00FF00FF00000000111111110000000011111111
0x0000FFFF00000000000000001111111111111111

在统计每个x的二进制位有多少个1时,使用了上述五个掩码(mask), 这五个掩码的二进制值是按照 ** 01 -> 0011 -> 00001111 -> 0000000011111111 -> 00000000000000001111111111111111** 的规律变化的,而偏移量(shift)的值也与1的数目相关 1 -> 2 -> 4 -> 8 -> 16

而根据表达式 x = (x & mask[i])+ ((x >> shift) & mask[i]); 可以看出: 第一次是计算每两位的和并储存在两个bit内, 第二次是计算每四位的和并储存在4个bit内…以此类推,最后计算每32位(即x)的和并储存在32个bit内(即x所在内存块),由于是二进制表示,32位的和就是1的个数。

ps: 这里要注意Max ops,如果用注释掉的部分的代码,会超出2个ops.

Problem 5

Code:

/* 
 * bang - Compute !x without using !
 *   Examples: bang(3) = 0, bang(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int bang(int x) {
        x = (x >> 16) | x;
        x = (x >> 8 ) | x;
        x = (x >> 4 ) | x;
        x = (x >> 2 ) | x;
        x = (x >> 1 ) | x;
        return (~x & 0x1);
}

Explaination:
若x为真,即其二进制形式中存在1,则经过五次x = (x >> shift) | x运算后,其最低位一定为1,反之为0,再通过(~x & 0x1)拿到对应的相反的值。

Problem 6

Code:

/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
  return 0x1 << 31;
}

Explaination:
32位有符号int型的最小值为0x80000000.

Problem 7

Code:

/* 
 * 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 offset = (x >> 31) & ((1 << n) + (~0));
        return (x + offset) >> n;
}

Explaination:
题目要求Round toward zero即正数向下取整,负数向上取整,因此需要先利用x >> 31判断是否为负数,若为负数则偏置这个值,在x 加上 offset(2 ^ n - 1)((x/y)向上取整 = ((x+y-1)/y)向下取整) 后右移n位就能实现Round toward zero

Problem 8

Code:

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
        return (~x) + 1;
}

Explaination:
2’s complement表示法即为对x 取反码+1

Problem 9

Code:

/*
 * 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));
}

Explaination:

  • 若x为0, 则直接返回0.
  • 否则返回x符号位取反的值,即x<0时返回0, x>0时返回1.

Problem 10

Code:

/*
 * 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 z = ((x + (~y)) >> 31);
        x = x >> 31;
        y = y >> 31;

        return !!((x | !y) & ((x & !y) | z));
}

Explaination:
先进行x-y-1,将结果右移31位获得其符号位,若运算未发生溢出,则符号位即为预期返回值。
然后获取x,y符号位

  • x | !y为False, 即x>0, y<0, 此时返回False.
  • 否则
    • x & !y为True, 即x<0, y>0, 此时返回True.
    • 否则x, y同号,x-y-1并不会溢出,因此返回其值。

Problem 11

Code:

/*
 * 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 floor = 0;

        floor += (!!(x >> (16 + floor))) << 4;
        floor += (!!(x >> (8  + floor))) << 3;
        floor += (!!(x >> (4  + floor))) << 2;
        floor += (!!(x >> (2  + floor))) << 1;
        floor += (!!(x >> (1  + floor))) << 0;

        return floor;
}

Explaination:
ilog2实际上是找正整数x的最高位的1. 解决方法就是二分法: floor += (!!(X >> (2 ^ n + floor))) << n.

Problem 12

Code:

/* 
 * 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) {
        if ((uf & 0x7f800000) == 0x7f800000 && (uf & 0x007fffff) != 0)
                return uf;
        return uf ^ 0x80000000;
}

Explaination:
先判断float值是否为NaN, 若为则直接返回uf, 若不为则改变符号位。

Problem 13

Code:

/* 
 * 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 sign_bit;
        unsigned e;
        unsigned m;
        unsigned tmp;
        unsigned truncated_num_lowest_bit;
        unsigned truncated_num_highest_bit;

        if (x == 0) {
                return 0;
        }

        sign_bit = x & 0x80000000;
        e = 0;
        m = x;

        if (sign_bit) {
                m = -x;
        }
        
        tmp = m;
        while (tmp)
        {
                tmp >>= 1;
                e++;
        }
        e -= 1;

        if (e <= 23) {
                m = (m << (23 - e)) & 0x007fffff;
        } else {
                truncated_num_lowest_bit = m & (-m);
                truncated_num_highest_bit = m & (1 << (e - 24));
                m = (m >> (e - 23));
                if (truncated_num_highest_bit) {
                        if (truncated_num_lowest_bit < truncated_num_highest_bit) {
                                m += 1;
                        } else {
                                if (m & 1) {
                                        m += 1;
                                }
                        }
                }
                if (m & 0x01000000) {
                        e += 1;
                }
                m = m & 0x007fffff;
        }

        return sign_bit | ((e + 127) << 23) | m;
}

Explaination:

  • 若x为0,直接返回0(因为绝对值小于1的浮点表示比较特殊,这里不需要自行转换,只会有0这一个特殊值,因此直接返回)
  • 首先获取最高符号位,如果符号位为1, 即x为负数,则令m=-x,否则m=x,使得m为x的绝对值。
  • 然后统计m位数,这里的e是指阶码,因此要减去1,表示要右移的位数。
    • 如果e<=23,则说明m会左移,不会截断二进制数,因此直接进行左移操作,使小数点后第一位对齐到23位。
    • 否则,有可能会产生截断,因此需要舍入,IEEE 754标准中,采用向偶数舍入的方法,因此进行else语句块内的一系列操作实现,注意,这里的半数边界为10…00
  • 最后整理符号位,阶码,尾数即可得到目标值。

Problem 14

Code:

/* 
 * 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 sign_bit = uf & 0x80000000;
        unsigned e = (uf & 0x7f800000) >> 23;
        unsigned m = uf & 0x007fffff;

        if (e == 0xff && m != 0)
                return uf;

        if (e == 0) {
                if (m & 0x00400000) {
                        e += 1;
                }
                m <<= 1;
        } else {
                e += 1;
        }
        m = m & 0x007fffff;
        if (e == 0x100) {
                e = 0xff;
        }

        return sign_bit | (e << 23) | m;
} 

Explaination:

  • 首先判断是否为NaN,若为NaN,则返回其本身。
  • 若为非规格化数,则左移m,如果m左移溢出,则e++。
  • 若为规格化数
    • 如果e不为最大阶数,则e++
  • 最后返回目标值。

Summarize

myarch% ./dlc bits.c
dlc:bits.c:143:bitAnd: 4 operators
dlc:bits.c:157:getByte: 5 operators
dlc:bits.c:169:logicalShift: 6 operators
dlc:bits.c:199:bitCount: 36 operators
dlc:bits.c:214:bang: 12 operators
dlc:bits.c:223:tmin: 1 operators
dlc:bits.c:238:fitsBits: 6 operators
dlc:bits.c:250:divpwr2: 7 operators
dlc:bits.c:260:negate: 2 operators
dlc:bits.c:270:isPositive: 4 operators
dlc:bits.c:284:isLessOrEqual: 13 operators
dlc:bits.c:302:ilog2: 30 operators
dlc:bits.c:318:float_neg: 6 operators
dlc:bits.c:378:float_i2f: 28 operators
dlc:bits.c:411:float_twice: 17 operators
myarch% ./btest
Score	Rating	Errors	Function
 1	1	0	bitAnd
 2	2	0	getByte
 3	3	0	logicalShift
 4	4	0	bitCount
 4	4	0	bang
 1	1	0	tmin
ERROR: Test fitsBits(-2147483648[0x80000000],32[0x20]) failed...
...Gives 1[0x1]. Should be 0[0x0]
 2	2	0	divpwr2
 2	2	0	negate
 3	3	0	isPositive
 3	3	0	isLessOrEqual
 4	4	0	ilog2
 2	2	0	float_neg
 4	4	0	float_i2f
 4	4	0	float_twice
Total points: 39/41

fitsBits的测试样例似乎有问题,因此没有给出。

  • 17
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值