./dlc bits.c
./btest
,如果某个函数错误,会显示错误的数据,以及正确的数据。由于本次实验基于dlc对bits.c进行编译,故需要先将老师给的文件夹datalab-handout添加到linux环境下,之后的实验将会基于这个文件夹进行操作
【datalab-handout下的README文件】
CS:APP数据实验室
给学生的路线
你的目标是修改你的bits.c副本,以便它通过所有
在不违反任何编码指南的情况下在btest中进行测试。
0.文件:
Makefile - 创建btest,fshow和ishow
自述文件 - 此文件
bits.c - 您将修改和处理的文件
bits.h - 头文件
btest.c - 主要的btest程序
btest.h - 用于构建btest
decl.c - 用于构建btest
tests.c - 用于构建btest
tests-header.c-用于构建btest
dlc * - 规则检查编译器二进制文件(数据实验室编译器)
driver.pl * - 使用btest和dlc来自动编程bits.c的驱动程序
Driverhdrs.pm - 可选“Beat the Prof”比赛的头文件
fshow.c - 用于检查浮点表示的实用程序
ishow.c - 用于检查整数表示的实用程序
重要信息:在开始之前请仔细阅读bits.c文件中的说明;这些给出了获得满分你所需要遵循的编码规则
使用dlc编译器(./dlc)自动检查您的版本
bits.c是否符合编码指南:
unix> ./dlc bits.c
如果您的代码没有问题,dlc将以默认方式返回。否则,它会打印标记任何问题的消息。
用dlc运行-e开关:
unix> ./dlc -e bits.c
使dlc打印每个函数使用的运算符数。
获得合法解决方案后,您可以使用./btest程序来测试其正确性
此目录中的Makefile将编译您的bits.c版本以及附加代码用于创建名为btest的程序(或测试工具)
要编译并运行btest程序,请输入:
unix> make btest
unix> ./btest [可选的cmd行args]
每次更改位时都需要重新编译btest程序。当你从一个平台移动到另一个平台时,你会想要摆脱旧版本的btest并生成一个新版本。使用命令:
unix> make clean
unix> make btest
Btest通过运行数百万次测试来测试代码的正确性
每个功能的案例。它测试着名角落周围的宽幅,例如Tmin和整数谜题为零,以及零,inf和浮动的非规格化数和规格化数之间的边界点难题。当btest检测到您的某个功能出错时,它打印出失败的测试、错误的结果和预期结果,然后终止该功能的测试。
以下是btest的命令行选项:
unix> ./btest -h
用法:./ btest [-hg] [-r
-1 指定第一个函数参数
-2 指定第二个函数参数
-3 指定第三个函数参数
-f 仅测试指定的函数
-g用于自动编辑的格式输出,没有错误消息
-h打印此消息
-r 为所有问题给出均匀的n重量
-T 将超时限制设置为lim
例子:
测试所有函数的正确性并打印出错误消息:
unix> ./btest
以紧凑的形式测试所有函数,没有错误消息:
unix> ./btest -g
测试函数foo的正确性:
unix> ./btest -f foo
使用特定参数测试函数foo的正确性:
unix> ./btest -f foo -1 27 -2 0xf
Btest不会检查您的代码是否符合编码要求准则。使用dlc来做到这一点。
3.助手计划
我们已经包含了ishow和fshow程序来帮助您破译,分别表示整数和浮点数据。每个需要一个单个十进制或十六进制数作为参数。要构建它们:
unix> make
示例用法:
unix> ./ishow 0x27
十六进制= 0x00000027,有符号= 39,无符号= 39
unix> ./ishow 27
十六进制= 0x0000001b,有符号= 27,无符号= 27
unix> ./fshow 0x15213243
浮点值3.255334057e-26,位表示0x15213243,符号= 0,指数= 0x2a,分数= 0x213243
标准化: +1.2593463659 X 2 ^( - 85)
linux> ./fshow 15213243
浮点值2.131829405e-38,位表示0x00e822bb,符号= 0,指数= 0x01,分数= 0x6822bb
标准化: +1.8135598898 X 2 ^( - 126)
第1步:仔细阅读以下说明。
您将通过编辑此源文件中的函数集合为数据实验室提供解决方案
整数编码规则:
用一个替换每个函数中的“return”语句或更多实现该功能的C代码行。你的代码必须符合以下风格:
int Funct(arg1,arg2,...){
/ *您的实施如何运作的简要说明* /
int var1 = Expr1;
...
int varM = ExprM;
varJ = ExprJ;
...
varN = ExprN;
返回ExprR;
}
每个“Expr”都是一个表达式,仅使用以下内容:
一些问题进一步限制了允许的运算符集合。
每个“Expr”可能包含多个运算符。你不局限于每行一个操作数。
明确禁止您:
你可以假设你的机器:
int pow2plus1(int x){
/ *利用移位能力计算2 * /的功率
return(1 << x)+ 1;
}
/ *
int pow2plus4(int x){
/ *利用移位能力计算2 * /的功率
int result =(1 << x);
result+ = 4;
return result;
}
浮点编码规则
对于需要您实现浮点运算的问题,编码规则不太严格。你被允许使用循环和条件语句。您可以同时使用整型和无符号整型;可以使用任意整数和无符号常量;可以使用循环和控制语句,使用INT和无符号
明确禁止您:
笔记:
/ *
*步骤2:根据编码规则修改以下功能。
*
*重要。为了避免分级惊喜:
/*
* bitAnd - x&y using only ~ and | 使用~和|实现位与操作
* Example: bitAnd(6, 5) = 4
* Legal ops: ~ |
* Max ops: 8
* Rating: 1
*/
int bitAnd(int x, int y) {
//使用摩根定律 a&b=~(~a|~b)
return ~(~x|~y);
}
/*
* getByte - Extract byte n from word x 从整型x中取出第n个字节
* 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) {
//从整型x中取出第n个字节,只需要x往右移n个字节(即右移n*8位)之后再&上0xFF即可
return (x>>(n<<3)) & 0xFF;
}
/*
* 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) {
//要实现逻辑右移的话,需要将算术右移得到的值&上00…11…(前面有n个零,后面都为1)
int arithShift=(x>>n); //先将x算术右移n位
int mask=((~(1<<31)>>n)<<1)+1; //产生一个mask值,补齐左移时添加的0,相当于右移n-1位
return arithShift & mask;
}
/*
* bitCount - returns count of number of 1's in word 求x中1的个数
* Examples: bitCount(5) = 2, bitCount(7) = 3
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 40
* Rating: 4
*/
int bitCount(int x) {
//本题采用分治的方法,先求出两个一组中1的个数,再求的4、8、16一组中1的个数
//将x的bit表示,分成两个一组,组内一位相加。
//再将x1的bit表示,分成四个一组,同样组内两位相加
//依次类推…
//注意:最后三次操作没有先经过mask,而是直接相加再mask,不是因为遵守分配律,
//而是因为前面2次要先mask再相加是因为算术右移产生1,要先把它mask成0。
//而经过第二次的四位一组,组内2位两位相加,表示bit 1的数量肯定不会超过4,即四位一组表示成0100。
//这样进行算术右移不会产生1,所以为了减少运算次数,可以先相加再mask
int mask1;
int mask2;
int mask4;
int mask8;
int mask16;
mask1 = 0x55 | 0x55 << 8;
mask1 = mask1 | mask1 << 16;
mask2 = 0x33 | 0x33 << 8;
mask2 = mask2 | mask2 << 16;
mask4 = 0x0F | 0x0F << 8;
mask4 = mask4 | mask4 << 16;
mask8 = 0xFF | 0xFF << 16;
mask16 = 0xFF | 0xFF << 8;
x = (x & mask1) + ((x >> 1) & mask1);
x = (x & mask2) + ((x >> 2) & mask2);
x = (x + (x >> 4)) & mask4;
x = (x + (x >> 8)) & mask8;
x = (x + (x >> 16)) & mask16;
return x;
}
/*
* bang - Compute !x without using ! 不用!符号求得!x
* Examples: bang(3) = 0, bang(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int bang(int x) {
//利用了补码相反数的特性,除了0,与0x80000000之外,其他的数与其相反数的符号肯定是不同的;
//0x80000000的相反数是它本身,所以只有0|0的相反数的最高位是0,其他数|相反数的最高位是1
//若(x|(~x+1))如果不是0的话,右移31位会得到0xFFFFFFFF,如果是0,则会得到0x0,最后加1即可得到!x
return ((x|(~x+1))>>31)+1;
}
/*
* tmin - return minimum two's complement integer//返回补码整数的最小整数数值
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
//补码整数的最小整数数值就是0x80000000,因此将1左移31位即可
return (1<<31);
}
/*
* fitsBits - return 1 if x can be represented as an
* n-bit, two's complement integer.如果x可以表示为n位二进制补码形式则返回1
* 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) {
//想要判断一个数是否能用更少的位数n来判断,只需要判断将该数进行符号扩展(先左移再右移)
//之后得到的数与原来的数是否相等(用异或来判断),若相等则返回1,否则为0
int shiftNumber = 32+(~n)+1; //32-n等于32+(n)'s
return !(x^((x<<shiftNumber)>>shiftNumber));
}
/*
* divpwr2 - Compute x/(2^n), for 0 <= n <= 30 计算x除以2的n次方
* 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) {
//如果x为正数,则直接右移n位,即偏移量为0;如果x为负数则需要加偏移量2^n-1之后再右移n位
int signx=(x>>31);
int bias=signx & ((1<<n)+(~0)); //其中~0为-1
return (x+bias)>>n;
}
/*
* negate - return -x 求相反数
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
* 按位取反,末位加1
*/
int negate(int x) {
return (~x)+1;
}
/*
* isPositive - return 1 if x > 0, return 0 otherwise 如果x为正数则返回1,否则返回0
* Example: isPositive(-1) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 8
* Rating: 3
*/
int isPositive(int x) {
//返回0的有两种情况,一种是x为零,一种为负数
//判断是否为负数时之需要判断符号位,因此可以进行符号扩展之后右移31,判断结果是否为1则为负数
return !((!x)|(x>>31));
}
/*
* isLessOrEqual - if x <= y then return 1, else return 0 判断x<=y则返回1,否则0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y) {
//要判断x<=y通常我们都会想到用y-x>=0来判断,这时候就碰到了一个问题,如果x与y异号且x为负时会发生溢出
//因此分情况讨论,如果x与y同号的话,看y-x的符号位(进行符号扩展右移31位)
//如果x与y异号的话,之需要根据x来判断,若x的符号位为1则y>=x
int signx=(x>>31) & 0x1; //求得x的符号位
int signy=(y>>31) & 0x1; //求得y的符号位
int is_notSame=(signx ^ signy); //判断x、y是否异号,进行异或操作,如果结果为1则异号,否则同号
int tmp=((y+(~x)+1)>>31) &0x1; //x与y同号时进行减法操作之后得到结果的符号位
return (((!tmp) & (!is_notSame))|(is_notSame & signx));
}
/*
* ilog2 - return floor(log base 2 of x), where x > 0 求以2为底x的对数
* Example: ilog2(16) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int ilog2(int x) {
//要求以2为底,x的对数,只需要知道最高的1在哪一位即可
//采用二分法,第一次右移16位,判断结果是否大于0,进行两次!!变为0/1之后左移4位
//第二次右移8+bias位,判断结果是否大于零,进行两次!!变为0/1之后左移3位
//依次分下去
int bitsNumber;
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)));
return bitsNumber;
}
/*
* float_neg - Return bit-level equivalent of expression -f for
* floating point argument f. 返回和浮点数参数-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) {
//在最高位置为0情况下,如果大于0x7F800000,一定是NaN,直接返回。如果不大于符号为取反,返回。
unsigned tmp;
unsigned result;
result = uf ^ (0x80 <<24); //让符号位取反
//如果最高位置为0时,需要判断它与0x7F800000的大小
tmp = uf & 0x7FFFFFFF;
if(tmp > 0x7F800000) result = uf;
return result;
}
/*
* float_i2f - Return bit-level equivalent of expression (float) x 返回int 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. 返回的是unsigned型但是表示的时二进制单精度形式
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned float_i2f(int x) {
unsigned shiftLeft;
unsigned afterShift,tmp;
unsigned absX;
unsigned sign;
unsigned flag;
absX = x;
shiftLeft = 0;
sign = 0;
if(!x) return 0;
if(x<0)
{
sign = 0x80000000; //先取其符号位
absX = -x;
}
//再将剩余部分全部取为正数形式,即absx,即可以得到无符号的数值
//然后将有数字的部分直接移动到最高位,记录移动的位数,再将其移动9位(因尾数只要23即可)。
afterShift = absX;
while(1)
{
tmp = afterShift;
afterShift <<= 1; //左移1位
shiftLeft++;
if(tmp & 0x80000000) break;
}
//对于阶码部分,所以E=32-shiftleft,bias为127,加上为159,if部分做舍入处理
if((afterShift & 0x01ff) >0x0100)
flag = 1;
else if((afterShift & 0x03ff) ==0x0300)
flag = 1;
else
flag = 0;
return (sign |(afterShift >> 9)|((159-shiftLeft) << 23)) + flag;
}
/*
* float_twice - Return bit-level equivalent of expression 2*f for
* floating point argument f. 返回以unsinged表示的浮点数二进制的二倍的二进制unsigned型
* 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) {
//如果阶码为0,那么就是非规格数,直接将尾数左移1位到阶码域上,其他不变即可
//例如 0 00000000 1000…001 变成 0 00000001 000…0010
//这样可以做的原因正是由于非规格化数的指数E = 1 - bias,而不是-bias
//这样使得可以平滑地从非规格数过渡到规格化数。
//如果阶码不为0且不是255,那么直接阶码加1即可。
//如果阶码为255,那么是NaN,∞,-∞,直接返回。
if((uf & 0x7F800000)==0)
uf = (((uf & 0x007FFFFF)<<1)|(uf & 0x80000000));
else if((uf & 0x7F800000)!=0x7F800000)
uf = uf + 0x00800000;
return uf;
}
一、将目录切换到dlc所在的文件下,输入./dlc bits.c对bits.c
文件中的函数进行编译,检查错误
由此可以看出,我的函数中使用了没有定义的变量signy
将sighy改为signy之后再使用dlc对bits.c进行编译,编译通过,即书写合法
二、输入./dlc -e bits.c
使dlc打印每个函数使用的运算符数。
经验证,各函数使用的操作数符合要求
三、用btest测试,输入make来编译btest
可知此时以产生了可执行文件fshow和ishow
四、使用./btest
来检查测试所有函数的正确性并打印出错误消息;输入./btest -g以紧凑的形式测试所有函数,不会显示错误消息
紧凑形式输出
可以看出此时bitCount函数有错误,使用 ./btest -f foo
测试函数foo的正确性;
将错误改正之后再次检查,正确
可以使用./ishow
和./fshow 十六进制
来显示计算机中表示的整型或浮点型数值