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:
十六进制 | 二进制 |
---|---|
0x55555555 | 01010101010101010101010101010101 |
0x33333333 | 00110011001100110011001100110011 |
0x0F0F0F0F | 00001111000011110000111100001111 |
0x00FF00FF | 00000000111111110000000011111111 |
0x0000FFFF | 00000000000000001111111111111111 |
在统计每个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
的测试样例似乎有问题,因此没有给出。