一、实验目的
本实验目的是加强学生对位级运算的理解及熟练使用的能力。
二、报告要求
本报告要求学生把实验中实现的所有函数逐一进行分析说明,写出实现的依据,也就是推理过程,可以是一个简单的数学证明,也可以是代码分析,根据实现中你的想法不同而异。
三、函数分析
bitXor函数
函数要求:
函数名 | bitXor |
---|---|
参数 | int , int |
功能实现 | x^y |
要求 | 只能使用 ~ 和 |
实现分析:
a^b = (a & ~b) | (~a & b), 为了将|转变为&, 对这个式子做两次取反即可。得到~(~ (a & ~b) & ~ (~a & b))
函数实现:
int bitXor(int x, int y) {
return ~(~(x & ~y) & ~(~x & y));
}
getByte函数
函数要求:
函数名 | getByte |
---|---|
参数 | int, int |
功能实现 | 从x中取出第n个字节 |
要求 | 使用: ! ~ & ^ | + << >> |
**分析:**通过x >> (n << 3)移动字节(即 n ∗ 2 3 n * 2^3 n∗23位),至前8位,实现目标功能
函数实现:
int getByte(int x, int n) {
int ret;
int t = x >> (n << 3);
ret = t & 0xFF;
return ret;
}
logicalShift函数
函数要求:
函数名 | logicalShift |
---|---|
参数 | int , int |
功能实现 | 逻辑右移 |
要求 | ! ~ & ^ | + << >> |
**分析:**通过变量va构建一个32位数,前n位都是0,后面的32-n位都是1,与原x进行&运算,得到结果。
函数实现:
int logicalShift(int x, int n) {
int va = (0x1 << 31) >> n;
x = x >> n;
va = ~(va << 1);
return (va & x);
}
bitCount函数
函数要求:
函数名 | bitCount |
---|---|
参数 | int |
功能实现 | 计算int型数据x的二进制串中1的个数 |
要求 | ! ~ & ^ | + << >> |
分析:
通过二分法,通过x与010101…0101, 00110011…0011,00001111…00001111…000…00111…111做&运算,依次计算 2 n ( n = 1 , 2 , 3 , 4 , 5 ) 2^n(n = 1,2,3,4,5) 2n(n=1,2,3,4,5)位中1的个数,每次计算时都利用到前一次的结果,进行加和得到最终32位数中1的个数。
首先将int型数据x的32位分成16组,并进行X31+X30,X29+X28,…,X3+X2,X1+X0的运算;然后将x分成8组,并进行X31+X30+X29+X28,…,X3+X2+X1+X0的运算。依次类推, 最后只剩下1组,此时将所有的位进行相加即得到了最终结果。
函数实现:
int bitCount(int x) {
int mask_1 = 0x55 | (0x55 << 8);
int mask_2=0x33|(0x33<<8);
int mask_4=0x0f|(0x0f<<8);
int mask_8=0xff|(0xff<<16);
int mask_16=0xff|(0xff<<8);
mask_1 = mask_1 | (mask_1 << 16);
mask_2=mask_2|(mask_2<<16);
mask_4=mask_4|(mask_4<<16);
x = (x & mask_1) + ((x >> 1) & mask_1);
x = (x & mask_2) + ((x >> 2) & mask_2);
x = (x & mask_4) + ((x >> 4) & mask_4);
x = (x & mask_8) + ((x >> 8) & mask_8);
x = (x & mask_16) + ((x >> 16) & mask_16);
return x;
}
conditional函数
函数要求:
函数名 | conditional |
---|---|
参数 | int int int |
功能实现 | 类似于C语言中的 x ? y : z |
要求 | ! ~ & ^ | + << >> |
**分析:**首先通过x = !!x把x转换成1或0,再将x转换成-1和0(因为-1的补码表示全为1),通过x得到最后取得y还是取得z。
函数实现:
int conditional(int x, int y, int z) {
x = !!x;
x = ~x + 1;
return ((x & y) | (~x & z));
}
tmin函数
函数要求:
函数名 | tmin |
---|---|
参数 | Null |
功能实现 | 返回最小的补码 |
要求 | ! ~ & ^ | + << >> |
**分析:**0x1左移31位得到结果
函数实现:
int tmin(void) {
return (0x1 << 31);
}
fitsBits函数
函数要求:
函数名 | fitsBits |
---|---|
参数 | int int |
功能实现 | x的补码是否可以表示成n位 |
要求 | ! ~ & ^ | + << >> |
**分析:**将x左移(32-n)位,再右移(32-n)位,再检查此时的数字是否与x相同,如果相同即符合条件,否则不符合。
函数实现:
int fitsBits(int x, int n) {
int shift = ~n + 33;// 32 - n = ~n + 1 + 32
int tmp = (x << shift) >> shift;
tmp = tmp + ~x + 1;
tmp = !!tmp;
return !tmp;
}
dividePower2函数
函数要求:
函数名 | dividePower2 |
---|---|
参数 | int int |
功能实现 | 计算x/(2^n) |
要求 | ! ~ & ^ |+ << >> |
分析:
根据定义,如果是正数,直接右移n位。如果是负数,则要加上一个偏置量 2 k − 1 2^k-1 2k−1。
函数实现:
int dividePower2(int x, int n) {
int sign = 0,var = 0;
sign = x >> 31;
var = (1 << n) + (~0);
return (x + (sign & var)) >> n;
}
negate函数
函数要求:
函数名 | negate |
---|---|
参数 | int |
功能实现 | 不用负号得到 |
要求 | ! ~ & ^ |+ << >> |
**分析:**根据定义,补码倒数取反加一。
函数实现:
int negate(int x) {
return (~x + 1);
}
howManyBits函数
函数要求:
函数名 | howManyBits |
---|---|
参数 | int |
功能实现 | 计算表达 x 所需的最少位数 |
要求 | ! ~ & ^ |+ << >> |
**分析:**问题的关键是计算这个数第一个“1”出现在第几位,再加上1个符号即可得到。
找到第一个1的方法是二分查找,先将32位一分为2,计算第一个1是在前16位,还是在后16位。然后将这16位移到前16位,再查找是前8位还是后8位…最终将每次搜索的结果进行相加,加上一个符号位得到结果。
int howManyBits(int x) {
int sign = x >> 31;// 0 -1
int ct_16 = 0;
int ct_8 = 0;
int ct_4 = 0;
int ct_2 = 0;
int ct_1 = 0;
int ct_0 = 0;
int fla = 0;
x = (sign & ~x) | (~sign & x);
sign = ~sign + 1;
fla = !!(x >> 16);
ct_16 = fla << 4;
x = x >> ct_16;
fla = !!(x >> 8);
ct_8 = fla << 3;
x = x >> ct_8;
fla = !!(x >> 4);
ct_4 = fla << 2;
x = x >> ct_4;
fla = !!(x >> 2);
ct_2 = fla << 1;
x = x >> ct_2;
fla = !!(x >> 1);
ct_1 = fla;
x = x >> ct_1;
ct_0 = x;
return ct_0 + ct_1 + ct_2 + ct_4 + ct_8 + ct_16 + 1;
}
isLessOrEqual函数
函数要求:
函数名 | isLessOrEqual |
---|---|
参数 | int int |
功能实现 | x <= y? |
要求 | ! ~ & ^ |+ << >> |
**分析:**通过判断y - x大于零还是小于零,得到结果。将y - x 转换为y + ~x + 1, 再判断溢出的特殊情况:y > 0 , x < 0 和 x > 0 , y < 0的情况。
int isLessOrEqual(int x, int y) {
int a = y + ~x + 1;
int sign = a >> 31;
int s_x = x >> 31;
int s_y = y >> 31;
int of_1 = s_x & (!s_y);// y > 0, x < 0
int of_2 = (!s_x) & (s_y);// x > 0, y < 0
return of_1 | ((!of_2) & (!sign));
}
intLog2函数
函数要求:
函数名 | intLog2 |
---|---|
参数 | int |
功能实现 | 计算⌊log2(x)⌋(向下取整) |
要求 | ! ~ & ^ |+ << >> |
**分析:**与howmanybits那题相似,关键是要找到最大的1位所在的位置,取log即为二分法叠加的结果减1。
函数实现:
int intLog2(int x) {
int ax = 1;
int cnt_16 = 0, cnt_8 = 0, cnt_4 = 0, cnt_2 = 0, cnt_1 = 0, cnt_0 = 0;
int flag = 0;
flag = !!(x >> 16);
cnt_16 = flag << 4;
x = x >> cnt_16;
flag = !!(x >> 8);
cnt_8 = flag << 3;
x = x >> cnt_8;
flag = !!(x >> 4);
cnt_4 = flag << 2;
x = x >> cnt_4;
flag = !!(x >> 2);
cnt_2 = flag << 1;
x = x >> cnt_2;
flag = !!(x >> 1);
cnt_1 = flag;
x = x >> cnt_1;
cnt_0 = x;
return cnt_0 + cnt_1 + cnt_2 + cnt_4 + cnt_8 + cnt_16 + (~ax) + 1;
}
floatAbsVal函数
函数要求:
函数名 | floatAbsVal |
---|---|
参数 | unsigned |
功能实现 | 计算f的绝对值的位级表示 |
要求 | || &&. also if, while |
**分析:**分类讨论,除了NaN的情况,其他情况直接将s位改成0,NaN的情况(满足exp == 255且frac != 0)直接返回NaN本身。
函数实现:
unsigned floatAbsVal(unsigned uf) {
if(((uf & 0x7F800000) >> 23) == 255 && (uf & 0x007FFFFF))
return uf;
else
return uf & 0x7FFFFFFF;
}
floatScale1d2函数
函数要求:
函数名 | floatScale1d2 |
---|---|
参数 | unsigned |
功能实现 | 计算0.5*f的位级表示 |
要求 | &&. also if, while |
**分析:**先将s,exp,frac位分离出来,分类:当exp = 255返回无穷,当exp > 1的其他情况,属于规格化的数,根据IEEE浮点表示 ( − 1 ) s ∗ M ∗ 2 E (-1)^s*M*2^E (−1)s∗M∗2E,只需要将exp-1即可得到结果。当exp小于等于1的时候,考虑到frac前两位的舍入,然后将原数右移一位即可。
函数实现:
unsigned floatScale1d2(unsigned uf) {
int exp = (uf&0x7F800000) >> 23;
int s = uf&0x80000000;
int frac = uf & 0x7FFFFF;
// NaN || infinity(INF)
if(exp == 255){
return uf;
}else if(exp > 1){
exp = exp - 1;
return s | (exp << 23) | frac;
}else if((uf & 0x3) == 0x3){
uf = uf + 0x2;
}
return ((uf >> 1) & 0x007fffff) | s;
}
floatFloat2Int函数
函数要求:
函数名 | floatFloat2Int |
---|---|
参数 | unsigned |
功能实现 | 计算(int)f的位级表示 |
要求 | &&. also if, while |
**分析:**首先提出s,exp,frac位,然后将exp-bias得到E, 分类讨论:当E小于0时,表示0附近的小数,此时直接返回0即可,当E > 31时,已经超过了int可表示的最大值,此时根据题意返回0x80000000u。由于frac最多23位,当E在0到23时,frac右移(23-E)即可得到结果,当E在23到31,frac左移(E-23)即可得到结果。
函数要求:
int floatFloat2Int(unsigned uf) {
int s = uf & 0x80000000;
int exp = (uf & 0x7f800000) >> 23;
int frac = (uf & 0x007fffff) | 0x00800000;// M = f + 1
int result = 0;
int E = exp - 127;
if(E < 0)
return 0;
else if(E > 31)
return 0x80000000u;
else if(E > 23){
result = frac << (E - 23);
}else{
result = frac >> (23 - E);
}
if (s)
return (~result) + 1;
else
return result;
}
四、实验总结
温故了CSAPP第二章的内容,并进行了实践。其中在CourseGrading系统中遇到了一个比较有意思的问题,就是如果在函数的中间声明变量的时候, 在./dlc bits.c
的时候会报parse error的错误,修改的方式是:在编写其他语句以前,先将所有的变量进行声明。
在做实验的过程中还要考虑到各种符号的限制。