CSAPP实验一:位操作(Data Lab)
Posted Jinze_L
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CSAPP实验一:位操作(Data Lab)相关的知识,希望对你有一定的参考价值。
本系列文章为中国科学技术大学计算机专业学科基础课《计算机系统》布置的实验,上课所用教材和内容为黑书CSAPP,当时花费很大精力和弯路,现来总结下各个实验,本文章为第一个实验——位操作(DataLab)。
一、实验名称:位操作
二、实验学时: 3
三、实验内容和目的
1.了解各种数据类型在计算机中的表示方法
2.掌握C语言数据类型的位级表示及操作
3.本实验总共包括有关位操作的15个编程题
4.你们的目标就是实现这15个编程题
5.所有要实现的代码都在bits.c里面
四、实验原理
1. <<:左移就是: 丢弃最高位,0补最低位 (算术和逻辑都是这样)
2. >>:再说右移,明白了左移的道理,那么右移就比较好理解了.
右移的概念和左移相反,就是往右边挪动若干位,运算符是>>.
右移对符号位的处理和左移不同,对于有符号整数来说,比如int类型,右移会保持符号位不变, 例如:
int i = 0x80000000;
i = i >> 1; //i的值不会变成0x40000000,而会变成0xc0000000
就是说,符号位向右移动后,正数的话补0,负数补1,也就是汇编语言中的算术右移。同样当移动的位数超过类型的长度时,会取余数,然后移动余数个位。
负数10100110 >>5(假设字长为8位),则得到的是 11111101
总之,在C中,左移是逻辑/算术左移(两者完全相同),右移是算术右移,会保持符号位不变 .实际应用中可以根据情况用左/右移做快速的乘 /除运算,这样会比循环效率高很多.
3. !:逻辑非
4. ˜ :位非
5. & :位与
6. ˆ:异或
7. | :位或
五、实验步骤及结果
1. tar xvf datalab-handout.tar解压代码,包含如下文件
Bits.c:唯一需要修改的文件
Btest.c :该文件的作用是对我们实现的bits.c功能的正确性行评估,
README:关于btest的一些说明。
Dlc:语法检查
2. 打开bits.c文件,该文件包含了一个结构体team,我们需要首先补充该team中的数据
3. Bits.c中包含需要实现的15个函数,文件中规定了实现每个函数需要的逻辑和算术操作符(规定数量)。
只能使用规定的操作符! ˜ & ˆ | + << >>
不能使用循环或者条件语句
不能使用超过8位的常数(ff)
4. 完成后用./dlc bits.c检查bits.c的语法是否正确,就是是否按照要求使用规定数量的操作符
5. 使用终端,执行make clean && make btest编译文件,使用终端,执行./btest,检查自己是否做对了,最终运行./driver.pl获得打分
6.实验思路以及程序代码(思路写在注释里面):
/*
* bitXor - x^y using only ~ and &
* Example: bitXor(4, 5) = 1
* Legal ops: ~ &
* Max ops: 14
* Rating: 1
* 只使用两种位运算实现异或操作。
*
异或,相同为0,相异为1。相同的话,不是11就是00。
x & y;就是找出11,(~x) & (~y);就是找出00。
那么正确答案就是,既不是11,也不是00
*/
int bitXor(int x, int y)
return ~(~x & ~y) & ~(x & y);
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
* 使用位运算获取对2补码的最小 int 值
*
Tmin就是最小的那个负数值,比较特殊,因为没有和它互为相反的正值。
补码是 0x80000000, 所以直接对1进行左移就好了
*/
int tmin(void)
return 0x1 << 31;
//2
/*
* isTmax - returns 1 if x is the maximum, two's complement number,
* and 0 otherwise
* Legal ops: ! ~ & ^ | +
* Max ops: 10
* Rating: 1
* 通过位运算计算是否是补码最大值
*
x判断是否为补码最大值,那么就需要经过某种特定的步骤把它转化为零。
我们发现最大值+最小值=一个特殊的值,即0xFFFF FFFF,用二进制表示它会是全一,取反之后就是全零
*/
int isTmax(int x)
int i = x + 1; // 这里取极端情况,当x就是最大值的时候,加一就变为最小值
x = x + i; // 最大值+最小值得到全一,0xFFFF FFFF
x = ~x; // 取反得全零。0x0000 0000
// 然而到这里还没有结束,发现0xFFFF FFFF,也是特殊情况之一,它加一等于全零了,而他本身是全一的情况,所以我们还得排除这个0xFFFF FFFF。到此我们的答案就出来了~
i = !i; // 给i取反,一个非零的布尔值都是true,也就是1
x = x + i; // 所以当给x+i的时候,要么加0要么就加1,它就会影响到下面的结果
return !x; // 得到的结果是相反的,还得!一下
/*
* allOddBits - return 1 if all odd-numbered bits in word set to 1
* where bits are numbered from 0 (least significant) to 31 (most significant)
* Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 2
* 判断所有奇数位是否都为1,这里的奇数指的是位的阶级是2的几次幂。重在思考转换规律,如何转换为对应的布尔值。
*
这个题目还是比较简单的,采用掩码方式解决。首先要构造掩码,使用移位运算符构造出奇数位全1的数 mask ,0xAA代表的是1010 1010,所以我们将8个A拼起来就行了,即0xAAAAAAAA
然后获取输入 x 值的奇数位,其他位清零(mask&x),即x=x&0xAAAAAAAA,将这个数偶数位为变为0
然后与 mask 进行异或操作,若相同则最终结果为0,然后返回其值的逻辑非。
*/
int allOddBits(int x)
int mask = 0xAA | 0xAA << 8;
mask = mask | mask << 16;
x = x & mask;
return !(mask ^ x);
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*
* 不使用 - 操作符,求 -x 值。这个题目是常识
*
*/
int negate(int x)
return ~x + 1;
//3
/*
* isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
* Example: isAsciiDigit(0x35) = 1.
* isAsciiDigit(0x3a) = 0.
* isAsciiDigit(0x05) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 3
*
* 计算输入值是否是数字 0-9 的 ASCII 值
*
x需要>=’0’且<=’9’,将x与临界点作差,然后判断符号位的为0还是1即可
48 - 57为ascii码数字部分,右边用58是因为0的符号位算正数,所以要多减一次
*/
int isAsciiDigit(int x)
return (!((x + ~48 + 1) >> 31)) & !!((x + ~58 + 1) >> 31);
/*
* conditional - same as x ? y : z
* Example: conditional(2,4,5) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*
* 使用位级运算实现C语言中的 x?y:z 三目运算符
*
如果根据 x 的布尔值转换为全0或全1更容易解决了,即 x==0 时位表示是全0的, x!=0 时位表示是全1的。
这就是1-2行代码,通过获取其布尔值0或1,然后求其补码(0的补码是本身,位表示全0;1的补码是-1,位表示全1)得到想要的结果。
然后通过位运算获取最终值。
*/
int conditional(int x, int y, int z)
x = !!x;
x = ~x + 1;
return (x & y) | (~x & z);
/*
* 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 negX = ~x + 1; //-x
int addX = negX + y; //y-x
int checkSign = addX >> 31 & 1; //y-x的符号
int leftBit = 1 << 31; //最大位为1的32位有符号数
int xLeft = x & leftBit; //x的符号
int yLeft = y & leftBit; //y的符号
int bitXor = xLeft ^ yLeft; //x和y符号相同标志位,相同为0不同为1
bitXor = (bitXor >> 31) & 1; //符号相同标志位格式化为0或1
return ((!bitXor) & (!checkSign)) | (bitXor & (xLeft >> 31)); //返回1有两种情况:符号相同标志位为0(相同)位与 y-x 的符号为0(y-x>=0)结果为1;符号相同标志位为1(不同)位与x的符号位为1(x<0)
//4
/*
* logicalNeg - implement the ! operator, using all of
* the legal operators except !
* Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
* 使用位级运算求逻辑非 !
*
逻辑非就是非0为1,非非0为0。利用其补码(取反加一)的性质,除了0和最小数(符号位为1,其余为0),外其他数都是互为相反数关系(符号位取位或为1)。
0和最小数的补码是本身,不过0的符号位与其补码符号位位或为0,最小数的为1。利用这一点得到解决方法。
*/
int logicalNeg(int x)
return ((x | (~x + 1)) >> 31) + 1;
/* howManyBits - return the minimum number of bits required to represent x in
* two's complement
* Examples: howManyBits(12) = 5
* howManyBits(298) = 10
* howManyBits(-5) = 4
* howManyBits(0) = 1
* howManyBits(-1) = 1
* howManyBits(0x80000000) = 32
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*
* 一个数用补码表示最少需要几位?
*/
int howManyBits(int x)
int b16, b8, b4, b2, b1, b0;
int sign = x >> 31;
x = (sign & ~x) | (~sign & x); //如果x为正则不变,否则按位取反(这样好找最高位为1的,原来是最高位为0的,这样也将符号位去掉了)
// 不断缩小范围
b16 = !!(x >> 16) << 4; //高十六位是否有1
x = x >> b16; //如果有(至少需要16位),则将原数右移16位
b8 = !!(x >> 8) << 3; //剩余位高8位是否有1
x = x >> b8; //如果有(至少需要16+8=24位),则右移8位
b4 = !!(x >> 4) << 2; //同理
x = x >> b4;
b2 = !!(x >> 2) << 1;
x = x >> b2;
b1 = !!(x >> 1);
x = x >> b1;
b0 = x;
return b16 + b8 + b4 + b2 + b1 + b0 + 1; //+1表示加上符号位
//float
/*
* floatScale2 - 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
* 求2乘一个浮点数
*
1.若原数为非规格化小数或0时,处理小数部分
* if(exp_ == 0) return (uf<<1)|s_;
* 2.若为NaN或INF时
* if(exp_ == 255) return uf;
* 直接返回。
* 3.若为其他情况,即指数加一
* ++exp_,
* 4.若加了之后为INF时,保证其不为NaN,即小数部分全为0,
* if(exp_ == 255) return 0x7f800000|s_;
* 5.最后为一般情况,直接输出2*f
* return (uf&0x807fffff)|(exp_<<23);
*/
unsigned floatScale2(unsigned uf)
int exp_ = (uf & 0x7f800000) >> 23;
int s_ = uf & 0x80000000;
if (exp_ == 0)
return (uf << 1) | s_;
if (exp_ == 255)
return uf;
++exp_;
if (exp_ == 255)
return 0x7f800000 | s_;
return (uf & 0x807fffff) | (exp_ << 23);
/*
* floatFloat2Int - Return bit-level equivalent of expression (int) f
* for floating point argument f.
* Argument is passed as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point value.
* Anything out of range (including NaN and infinity) should return
* 0x80000000u.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*
* 将浮点数转换为有符号整数,float_f2i
*
* 思路:
* 先将浮点数分成三段,
* 符号部分s_ = uf>>31,
* 指数大小exp_ = ((uf&0x7f800000)>>23)-127,
* 获取小数部分,并补上浮点数缺省的1,
* frac_ = (uf&0x007fffff)|0x00800000。
* 处理特殊情况:
* 若全为0是返回0,
* 若指数大于31,整数无法表示溢出返回0x80000000。
* 若指数小于0,该数0<x<1返回0
* 若指数部分大于23则将小数部分向左移动frac_ <<= (exp_ - 23) ,exp_代表指数大小。
* 若指数部分小于23则将小数部分向右移动frac_ >>= (23 - exp_) ,exp_代表指数大小。
* 考虑最后符号,正数转换为负数不会产生溢出。
* 若frac_为正数,则根据s_调整正负输出即可。
* 若frac_为负数,唯一正确情况为0x80000000。
*/
int floatFloat2Int(unsigned uf)
int s_ = uf >> 31;
int exp_ = ((uf & 0x7f800000) >> 23) - 127;
int frac_ = (uf & 0x007fffff) | 0x00800000;
if (!(uf & 0x7fffffff))
return 0;
if (exp_ > 31)
return 0x80000000;
if (exp_ < 0)
return 0;
if (exp_ > 23)
frac_ <<= (exp_ - 23);
else
frac_ >>= (23 - exp_);
if (!((frac_ >> 31) ^ s_))
return frac_;
else if (frac_ >> 31)
return 0x80000000;
else
return ~frac_ + 1;
/*
* floatPower2 - Return bit-level equivalent of the expression 2.0^x
* (2.0 raised to the power x) for any 32-bit integer x.
*
* The unsigned value that is returned should have the identical bit
* representation as the single-precision floating-point number 2.0^x.
* If the result is too small to be represented as a denorm, return
* 0. If too large, return +INF.
*
* Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while
* Max ops: 30
* Rating: 4
*
* 计算浮点数2.0^x
* 一般情况下,
* 把x当成浮点数的阶码就可以了,加 127 得到指数阶码,由于小数点后面都是 0,只需左移指数部分
* 考虑特殊情况,
* 指数exp<-127和 exp>128,分别返回0和0x7f800000
*/
unsigned floatPower2(int x)
if (x < -127)
return 0;
if (x > 128)
return 0x7f800000;
x += 127;
x = x << 23;
return x;
六、运行测试程序结果
以上是关于CSAPP实验一:位操作(Data Lab)的主要内容,如果未能解决你的问题,请参考以下文章