实验目的:
1.替换bits.c中各个函数中的return,格式要求如下所示:
int Funct(arg1, arg2, …) {
/* brief description of how your implementation works */
int var1 = Expr1;
…
int varM = ExprM;
varJ = ExprJ;
...
varN = ExprN;
return ExprR;
}
其中,每一个“Expr”只能使用如下规则:
① 数字只能使用0到255(0xff),不能使用像0xffffffff这样大的数字
② 函数参数和局部变量(没有全局变量)
③ 一元运算目:! ~
④ 二元运算目:& ^ | + << >>
2.bits.c中所给的15个函数都是缺失的,需要用上每个函数被允许的操作去实现所要求的功能。
3.下面的操作不被允许:
① 使用任何控制结构,如if, do, while, for, switch等。
② 定义或使用任何宏。
③ 在此文件中定义任何其他函数。
④ 调用任何库函数。
⑤ 使用任何其他的操作,如&&, ||, -, or ?:
⑥ 使用任何形式的casting
⑦ 使用除int以外的任何数据类型。这意味着你不能使用数组、结构等。
对于需要你执行浮点运算的问题,编码规则较不严格。允许使用循环和条件控制也可以同时使用int和unsigned。可以使用任意整数和无符号常量。
实验内容及操作步骤:
一、初步安装dlc:
由于第二次试验是基于dlc编译环境,所以需要先进行添加,即将本次实验的文件夹datalab-handout复制到linux环境下,之后的实验都是在这个文件夹下进行:
二、填写bits.c中实验需填写的内容,并进行简单的解释:
先对整个bits.c文件进行翻译,了解到要求是:
警告:请不要使用<stdio.h>头文件,他会使dlc编译器发生错误,你可以仍旧在不包含<stdio.h>头文件的情况下使用printf语句来调试,尽管你可能会有警告信息。一般情况,忽视警告信息是不对的,但在这里没有问题
你将会通过编译和调用在这个源文件中的函数来设计自己的代码,并最终向数据实验室提供你的解决方案。通过一行或多行的c语言代码来替代函数中的return语句并实现函数,你的代码必须满足下面几个规则:
- 0到255(包含)之间的整数常量,你不能使用太大的常数
- 功能语句和局部变量(不能使全局变量)
- 一元整数操作如!(逻辑非)和~(按位非)
- 二元整数操作如&(按位与)、^(异或)、|(或)、+(相加)、<<(算术左移)、>>(算术右移)
你不被允许以下操作: - 使用任何一个控制结构,像if,do,while,for,switch等等
- 定义或使用宏
- 在本文件中定义任何一个格外的函数
- 声明或调用任何函数
- 使用别的任何操作符,如&&、||、-或者?
- 使用任何形式的指针
- 使用任何除了int之外的数据类型,这里暗示你不能使用数组,结构体和类
你可以假定你的机器: - 使用2的补码表示负数,32位表示整数
- 执行算术右移
- 移动一个整数超过他的字长的时候会产生不可预测的后果
对于要执行浮点操作的问题,编码规则不那么严格,允许使用循环和条件控制即if和for语句,你可以使用int和unsigned,可以使用任意整数和无符号常量
你被禁止: - 定义或使用宏
- 在本文件中定义任何其他函数
- 声明或调用任何函数
- 使用任何形式的指针
5.使用除int和无符号以外的任何数据类型,意味着不能用数组,结构体或类 - 使用任何浮点数据类型,操作或常量
提示: - 使用dlc(数据实验室检查器)编译器(在讲义中描述)来检查解决方案的合法性
- 每一个函数都有一个最大数量的操作符,允许你在实现该函数时使用这些操作符。最大操作符计数由dlc检查,请注意“=”不计算在内,你可以随意使用运算符
- 使用btest测试工具来检查你的功能是否正确
- 使用bdd检查器正式的验证你的功能
- 每个函数的最大操作数在每个函数的头注释中给出,如果在编写和次文件中最大操作数之间存在不一致,请将此文件作为权威来源
三、对函数进行添加和重写:
1、
/*
* 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));
}
代码解释:使用~与|实现按位与,真值表为(每一位):
由于最高限制次数为8次,那么只能使用德摩根律,得到x&y=(X|~Y)即可。
2、
/*
* 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) {
return ((x>>(n<<3))&0xff);
}
代码解释:将一个32位数实现返回对应编号位置的数字,先将编号n转换为移动的位数,比如2就要移动2x2^3个位置,0则不需要移动,将所需要的数字移动到最低位,接着按位与0XFF,只取最低的两位,得到最终结果。按位与0xFF表示保留最后一个字节。
例:假设x=0x12345678,n=2
那么78是编号0,56编号为1,n移动3位后得到的结果是2x8=16位,即需要把x向右移动16位,算数逻辑均可。以算数为例,则x为0x00001234,取最后的两位即34,即为所求结果。
3、
/*
* 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) {
return ~((((1<<31)&x)>>n)<<1)&(x>>n);
}
代码解释:将32位数逻辑右移n位
逻辑右移是在左端补最高有效位的值,算术右移是直接补0,所以将1左移31位后得到最高位为1,其他全为0的数,与x相与后得到x的符号位,其他全为0的数,其再算术右移n位然后左移一位,有n个符号位剩余全为0的数,再取反得到n个符号位的相反位,其他全为1的数。这个数再与x算术右移n为的数进行相与,首先右端的数和1111相与后结果不变,而符号位会进行保留。
4、
/*
* 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 count;
int tmpMask1 = (0x55)|(0x55<<8);
int mask1 = (tmpMask1)|(tmpMask1<<16);
int tmpMask2 = (0x33)|(0x33<<8);
int mask2 = (tmpMask2)|(tmpMask2<<16);
int tmpMask3 = (0x0f)|(0x0f<<8);
int mask3 = (tmpMask3)|(tmpMask3<<16);
int mask4 = (0xff)|(0xff<<16);
int mask5 = (0xff)|(0xff<<8);
count = (x&mask1)+((x>>1)&mask1);
count = (count&mask2)+((count>>2)&mask2);
count = (count + (count >> 4)) & mask3;
count = (count + (count >> 8)) & mask4;
count = (count + (count >> 16)) & mask5;
return count;
}
代码解释:输入一个int型数字,输出该数字中机器码中有多少个1,即通过二分法进行查找记录即可,先计算每两位中1的个数,并用对应的两队来进行存储,然后计算每四位中1的个数,用对应的4位进行存储,最后得到16位中1的个数,即x中1的个数
5、
在这里插入代码片
代码解释:不用!而实现!功能。第一步~x+1求补码,与x本身进行相或后得到最高有效位,如果大于0那么最高有效位必为1,将该数逻辑右移31位后得到要么全零要么全一的数,然后与1相与得到一个相反的值就可以实现!的操作。
6、
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 1<<31;
}
代码解释:得到最小的二进制数,直接左移1即可
7、
/*
* fitsBits - return 1 if x can be represented as an
* n-bit, two's complement integer.
* 1 <= n <= 32
* Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 2
*/
int fitsBits(int x, int n) {
//get 32-n;
return !(((x >> (n+(~0))) + 1)>>1);
}
代码解释:判断x是否能被n位二进制数表示,即x是否在-2(n-1)到2(n-1)-1范围内
8、
/*
* 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) {
return (x+((x>>31)&((1<<n)+(~0))))>>n;
}
代码解释:x除以2的n次方,实际上直接右移n位即可,面对负数时需要加上一个偏移量
9、
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x+1;
}
代码解释:取x的相反数,直接返回~x+1即可
10、
/*
* 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>>31|(!x));
}
代码解释:如果x大于0则返回1,否则返回0,直接判断符号位,但需要特殊考虑0
11、
/*
* 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 signx=x>>31;
int signy=y>>31;
int signSame=((x+((~y)))>>31)&(!(signx^signy));
int signDiffer=signx&(!signy);
return signDiffer|signSame;
}
代码解释:如果x小于等于y,则返回1,否则返回0。先对符号位进行比较,第1,2行得到了x和y的符号位,第3行比较两者相减是否越界,第四行得到两者的符号位是否异号,最终返回两个结果的相或结果,只要有一者为1,那么就代表x小于等于y,否则x大于y
12、
/*
* 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 bitsNumber=0;
bitsNumber=(!!(x>>16))<<4;//
bitsNumber=bitsNumber+((!!(x>>(bitsNumber+8)))<<3);
bitsNumber=bitsNumber+((!!(x>>(bitsNumber+4)))<<2);
bitsNumber=bitsNumber+((!!(x>>(bitsNumber+2)))<<1);
bitsNumber=bitsNumber+(!!(x>>(bitsNumber+1)));
//for non zero bitsNumber, it should add 0
//for zero bitsNumber, it should subtract 1
bitsNumber=bitsNumber+(!!bitsNumber)+(~0)+(!(1^x));
//当x为0时,还需要减一才能得到正确值。
return bitsNumber;
}
代码解释:得到一个数是由多少二进制位表示,倒数第二行表示当x为0的时候,还需要进行减一才能得到正确值,是一个类似于分治法,逐步化简。
13、
/*
* 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) {
unsigned result;
unsigned tmp;
result=uf^0x80000000; //将符号位改反 -f
tmp=uf & (0x7fffffff);
if(tmp > 0x7f800000)//此时是NaN
result = uf;
return result;
}
代码解释:计算浮点数的相反数,直接将符号位改反即可,但需要判断NAN的情况,这里已经是浮点数类型了,可以使用if判断。
14、
/*
* 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 shiftLeft=0;
unsigned afterShift, tmp, flag;
unsigned absX=x;
unsigned sign=0;
//special case
if (x==0) return 0;
//if x < 0, sign = 1000...,abs_x = -x
if (x<0)
{
sign=0x80000000;
absX=-x;
}
afterShift=absX;
//count shift_left and after_shift
while (1)
{
tmp=afterShift;
afterShift<<=1;
shiftLeft++;
if (tmp & 0x80000000) break;//
}
if ((afterShift & 0x01ff)>0x0100)
flag=1;
else if ((afterShift & 0x03ff)==0x0300)
flag=1;
else
flag=0;
return sign + (afterShift>>9) + ((159-shiftLeft)<<23) + flag;
}
代码解释:将整形转化为无符号浮点数,即求浮点数。先取的符号位,再将剩余部分全部取为正数形式,即absx,即可以得到无符号的数值。然后将有数字的部分直接移动到最高位,记录移动的位数,再将其移动9位(因尾数只要23即可)。对于阶码部分,由于记录的是小数点从31位右数到第一个1,但实际上需要处理的是从第0位到第一位,所以E=32-shiftleft,bias为127,加上为159,if部分做舍入处理
15、
/*
* 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 f = uf;
if ((f & 0x7F800000) == 0) //
{
//左移一位
f = ((f & 0x007FFFFF) << 1) | (0x80000000 & f);//007fff---11111111111111111111111 23bit
}
else if ((f & 0x7F800000) != 0x7F800000)
{
f =f + 0x00800000; //100000 23
}
return f;
}
代码解释:将无符号浮点数乘2,对无阶码小数,对其尾部乘2即可,即直接左移一位,但要提前记录符号位。对于规格化数,直接对其阶码+1即可
四、实验结果及分析:
1、首先转移dlc所在的目录之下:
2、然后使用dlc检测bits.c是否有错误,代码行为:./dlc bits.c
结果显示没有错误,即书写规范
3、通过使用dlc的-e选项
显示各个操作数符合要求
4、使用btest检验函数实现代码的功能正确性,先使用make编译生成btest可执行程序,具体代码行为:make(make会对当前目录下的所有程序调用gcc进行编译)
几乎所有的.c文件都生成了相对应的可执行文件:
5、调用btest命令检查bits.c中的所有函数的功能的正确性,以便下一步查找错误原因,指令行为:./btest bits.c
至此,基本上所有实验已经完成,但我们可以继续练习一下btest的使用,例如测试所有函数的正确性并打印出错误信息。
结果基本相同
或者测试特有参数,如测试所有函数在不返回错误信息的紧凑型模式:
由于没有错误,所以实际上结果还是一样
或者测试特定函数名的函数,如./btest -f bitAnd
至此,实验的全部内容已经全部完成。