深入理解计算机系统data lab

Data Lab


Thank for watch
深入理解计算机系统data lab_第1张图片

实验梗概:

解决比特难题,整数操作问题和浮点问题
比特难题:

名字 描述 评分
bitAnd(x,y) 使用|和~完成x&y 1
getByte(x,n) 从x得到第n字节的数 2
logicalShift(x,n) 使用算数右移完成逻辑右移 3
bitCount(x) x中有多少个1 4
bang(x) 不用!实现!操作符 4

整数操作问题

名字 描述 评分
tmin() 返回最小补码 1
fitsBits(x,n) x能用n bits表示吗 2
divpwr2(x,n) 计算 x/(2^n) 2
negate(x) 不用负号完成取反 2
isPosition(x) x大于0吗 3
isLessOrEqual(x,y) x小于等于y吗 3
ilog2(x) 计算floor(log2(x)) 4

浮点问题

名字 描述 评分
float_neg(uf) 取反 2
float_i2f(x) 计算(float)x 4
float_twice(uf) 计算x*2.0 4

实验指南

完成实验你只需要修改bits.c.配套有btest,dlc和fshow配件。

在本实验中:
不能使用循环,分支语句。只允许使用顺序语句。

只能使用 ! ˜ & ˆ | + << >>(甚至更少)

只能使用小于256的常数。

只能使用int或unsigned,不能使用float,不能类型转换

在每个函数的注释有更清楚的任务要求

工具介绍

btest

这个工具检查bits.c里函数的正确性。(每次修改完bits.c都要重新make)
./btest 检查你的所有答案是否正确
./btest -f bitAnd 检查bitAnd是否正确。

dlc

./dlc bits.c 检查你使用了非法操作符,或过多的操作符,或循环,分支语句
./dlc -e bits.c 检查你每个函数使用的操作符数量


driver

./driver.pl 算出你的最后得分

解法

bitAnd

要求:使用**~**和 **|**完成 x&y
例:bitAnd(6,5)=4
允许操作 ~ |
操作符限定:8
分值:1


摩根定律的运用 ``` int bitAnd(int x, int y) { return ~(~x | ~y); } ```


### getByte 要求:从x中提取第n字节 例:bitAnd(0x12345678,1)=0x56 允许操作 **!** **~** **^** **|** **+** **<<** **>>** 操作符限定:6 分值:2

``` int getByte(int x, int n) { x=x>>(n<<3); return x&0xff; ``` }


### logicalShift 要求:用算数右移实现逻辑右移 例:logicalShift(0x87654321,4)=0x08765432 允许操作 **!** **~** **^** **&** **^** **+** **<<** **>>** 操作符限定:20 分值:3

``` int logicalShift(int x, int n) { int xsra = x >> n; int mask = ~0<<(32+~n)<<1; mask = ~mask; return xsra&mask; } ```


### bitCount 要求:计算x里有多少个1 例:bitAnd(5)=2 bitCount(7)=3 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:40 分值:4

首先将32位数分为8组,对于每四位数,通过三次移位运算统计每组数中1的个数,然后将前16位与后16位相加,将1的个数浓缩在16位中,再以同样的方法将1的个数整理到4位中,得到最后结果。· [类似思路](https://blog.csdn.net/weixin_41256413/article/details/79937907) ``` int bitCount(int x) { int i = 0x11 | (0x11 << 8); i = i + (i << 16); int sum = 0;
sum += x & i;
sum += (x >> 1) & i;
sum += (x >> 2) & i;
sum += (x >> 3) & i;

i = 0xff + (0xff << 8);
sum = (sum >> 16) + (sum & i);

i = 0x0f + (0x0f << 8);
sum = ((sum >> 4) & i) + (sum & i);

i = 0xff;
sum = (sum & i) + (sum>>8);

return sum;

}




### bang 要求:不用!实现!x 例:bang(3)=0,bang(0)=1 允许操作 **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:12 分值:4

如果不懂,可以看下面链接,有讲的很详细 [思路](https://blog.csdn.net/weixin_41256413/article/details/79937907)

int bang(int x) {
x = (x>>1)|x;
x = (x>>2)|x;
x = (x>>4)|x;
x = (x>>8)|x;
x = (x>>16)|x;
return ~(x & 1)+2;
}





### tmin 要求:返回最小补码 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:4 分值:1

int tmin(void) {
return 0x80<<24;
}




### fitsBits 要求:n bit能表示x吗 例:bitAnd(5,3)=0 bitCount(-4,3)=1 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:15 分值:2

这题其实考补码符号扩展,将x右移(n+~0)位,即得到了符号位
负数:0xffffffff正数0x0。

int fitsBits(int x, int n) {
x = x>>(n+~0);
return !x | !(x+1);
}




### divpwr2 要求:计算x/(2^n) 0<=n<=30,向0舍入 例:divpwr2(15,1)=7 divpwr2(-33,4)=-2 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:15 分值:2

这题,如果x小于0,要加上偏置。

int divpwr2(int x, int n) {
int sign = (x>>31);
x = x + (sign & ((1< return x >> n;
}




### negata 要求:取反 例:negata(1)=-1 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:5 分值:2

相信国产的书都是这么教补码的(道理在书上的web sides上。这里给出[原理网址](http://csapp.cs.cmu.edu/3e/waside/waside-tneg.pdf)所以不赘述了 int negate(int x) { return ~x + 1; }


### isPositive 要求:x大于0吗 例:isPositive(-1)=0 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:8 分值:3

把情况分为正数,0,负数就好懂了

int isPositive(int x) {
return !(x>>31) & !!x;
//or
//retunrn !(x>>31);
}




### isLessOrEqual 要求:y大于等于x吗 例:isLessOrEqual(4,5) = 1. 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:24 分值:3

equal是符号相同。在这个情况下,如果x更大,~y+x是0.。如果y更大。~y+x是1 nequal是符号不同.

int isLessOrEqual(int x, int y) {
unsigned sign_x = x>>31;
unsigned sign_y = y>>31;
unsigned equal = !(sign_x ^ sign_y) & ((x+~y)>>31);
unsigned nequal = sign_x & !sign_y;
return equal | nequal;
}




### ilog2 要求:计算floor(log2(x)) 例:ilog2(16) = 4 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:90 分值:4

这个问题可以转换为x最高位是几 前半段[思路](https://blog.csdn.net/weixin_41256413/article/details/79937907) 后半段确定最高位是第几位 bit_16确定前16位有没有。如果有。bit_16=16,且x右移16位, bit_8确定前8位有没有。如果有。bit_8=8,且x右移8位, bit_4确定前4位有没有。如果有。bit_4=4,且x右移4位, bit_2确定前2位有没有。如果有。bit_2=2,且x右移2位, bit_1确定前1位有没有。如果有。bit_1=1,且x右移1位, 最后相加,即可知道最高位是几

int ilog2(int x){
x|=x>>1;
x|=x>>2;
x|=x>>4;
x|=x>>8;
x|=x>>16;
x = x^(x>>1);
int bit_16,bit_8,bit_4,bit_2,bit_1;
bit_16 = !(!(x>>16))<<4;
x = x>>bit_16;
bit_8 = !(!(x>>8))<<3;
x = x>>bit_8;
bit_4 = !(!(x>>4))<<2;
x = x>>bit_4;
bit_2 = !(!(x>>2))<<1;
x = x>>bit_2;
bit_1 = !(!(x>>1));

return bit_16+bit_8+bit_4+bit_2+bit_1;

}




### float_neg 要求:取反,如果x是NaN,返回x 允许操作 循环,分支等等都能用,除了float类型和强制转换类型 操作符限定:10 分值:2

题目说了,注意NaN,那咱们就注意NaN

unsigned float_neg(unsigned uf){
unsigned exp = uf>>23 &0xFF;
unsigned frac = uf & 0x7FFFFF;
if(exp==0xFF && frac!=0)
return uf;
unsigned mask = 1<<31;
return uf ^ mask;
}




### float_i2f 要求:返回(float)x 例:ilog2(16) = 4 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:30 分值:4

首先:要找到该int的最高位数,这样确定位移的大小 当左移时,自然不用考虑舍入的问题。 当右移时,要考虑最大舍入+偶数舍入,这是最需要思考的地方

unsigned float_i2f(int x) {
if(x==0)
return 0;
unsigned sign = !!(x>>31);
if(x<0)
x = ~x+1;
unsigned exp =127;
unsigned frac =x;
//find the highest bit
unsigned j;
for (j = (sizeof(int) << 3) - 1; j >= 0; --j) {
if ((frac >> j) & 1)
break;
}
if(j<23){
frac <<=(23-j);
exp += j;
}
else{
frac>>=(j-23);
exp +=j;
unsigned mask = (1 << (j - 23)) - 1;
if ((x&mask) >(1 << (j - 24)))
frac++; //需要舍入到大值
else if ((x&mask) == 1 << (j - 24) && (frac & 1))
frac++; //舍入到偶数
if (frac == (1 << 24))
exp++; //舍入到偶数超过(1<<24) - 1,指数需要再加1
}
frac&=0x7FFFFF;
return (sign<<31) | (exp<<23) | frac;
}





### float_twice 要求:返回x*2.0,如果是NaN,返回x 允许操作 **!** **~** **&** **^** **|** **+** **<<** **>>** 操作符限定:30 分值:4

分情况讨论 f 非规格是 小数左移一位即时*2.0 f在最大非规格 用公式推出即可 f在规格数里。将exp+1即时*2 f在规格数里,exp已经到1111 1110.再乘2.0就变为了+∞ f位无穷大或者NaN,直接返回f

unsigned float_twice(unsigned uf) {
unsigned sign = uf>>31;
unsigned exp = uf>>23 & 0xFF;
unsigned frac = uf & 0x7FFFFF;

if(exp==0 && frac< 0x7FFFFF)
	frac <<=1;
else if(exp ==0 && frac==0x7FFFFF){
	exp+=1;
	frac = 0x7FFFFE;
}
else if(exp==0xFE){
	exp = 0xFF;
	frac=0;
}
else if(exp==0xFF)
	return uf;
else
	exp+=1;
return (sign<<31) | (exp<<23) | frac;

}

你可能感兴趣的:(csapp)