解决比特难题,整数操作问题和浮点问题
比特难题:
名字 | 描述 | 评分 |
---|---|---|
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,不能类型转换
在每个函数的注释有更清楚的任务要求
这个工具检查bits.c里函数的正确性。(每次修改完bits.c都要重新make)
./btest 检查你的所有答案是否正确
./btest -f bitAnd 检查bitAnd是否正确。
./dlc bits.c 检查你使用了非法操作符,或过多的操作符,或循环,分支语句
./dlc -e bits.c 检查你每个函数使用的操作符数量
./driver.pl 算出你的最后得分
要求:使用**~**和 **|**完成 x&y
例:bitAnd(6,5)=4
允许操作 ~ |
操作符限定:8
分值:1
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<
}
### 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;
}