App Data Lab——计算机系统实验lab2

实验题目

APP Data Lab

实验目的:

Your goal is to modify your copy of bits.c so that it passes all the tests in btest without violating any of the coding guidelines.
我的目标是修改bits.c文档,完成所有函数的编写,利用dlc和btest两个工具通过所有的测试

实验环境

ubuntu12.04环境

实验内容及操作步骤:

根据要求编写函数:

(1) bitAnd

函数功能:两个数的与操作
实现要求:

  • bitAnd - x&y using only ~ and |
  • Example: bitAnd(6, 5) = 4
  • Legal ops: ~ |
  • Max ops: 8
  • Rating: 1
    实现思路:利用摩尔定理,将x and y=(x or ~y)
    实现代码:
int bitAnd(int x, int y) {
  return ~((~x)|(~y));
}

(2) getByte

函数功能:取出一个int数据指定的字节
实现要求:

  • 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

实现思路:
首先我们将n左移3位,扩展到8的倍数,然后右移n<<3,去除字节
实现代码:

int getByte(int x, int n) {
  x=x>>(n<<3);
  return x&0xff;
}

(3) logicalShift

函数功能:逻辑右移
实现要求:

  • 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

实现思路:
LogicShift实现并不复杂,但是必须注意的是,是逻辑右移不死算数右移,也就是说,如果直接进行移动会导致符号位的填充现象。因此我们要消除因为符号位填充导致的错误,这里用temp=0x7fffffff,右移n-1位,然后与x>>n相与消除填充符号位
实现代码:

int logicalShift(int x, int n) {//注意是逻辑右移,不是算数右移,不能自动补符号位
  int temp;
  x=x>>n;
  temp=~(1<<31);//0x7fffffff
  temp=((temp>>n)<<1)+1;//右移n-1位,制造0000,用于消除符号位
  return x&temp;//消除最高符号位
}

(4) bitCount

函数功能:计算一个二进制数中1的个数
实现要求:

  • bitCount - returns count of number of 1’s in word
  • Examples: bitCount(5) = 2, bitCount(7) = 3
  • Legal ops: ! ~ & ^ | + << >>
  • Max ops: 40
  • Rating: 4

实现思路:
这里的做法我一开始没有想到,但是从网上了解到了一种思路,也就是二分法,我们首先用01相间的一组数据去与x做这样的操作x=(x&list2)+((x>>1)&list2),可以算出2位中1的个数,同样的我们用0011相间的一组数去与x做:x=(x&list4)+((x>>1)&list4)可以计算4位中的1,然后用00001111做同样的操作,得到8位中的1,最有右移x,计算每8位的结果。
实现代码:

int bitCount(int x) {
  int list2=(((((0x55<<8)+0x55)<<8)+0x55)<<8)+0x55;//构造0101 0101 0101 0101 0101 0101 0101 0101
  int list4=(((((0x33<<8)+0x33)<<8)+0x33)<<8)+0x33;//构造0011 0011 0011 0011 0011 0011 0011 0011
  int list8=(((((0x0f<<8)+0x0f)<<8)+0x0f)<<8)+0x0f;//构造0000 1111 0000 1111 0000 1111 0000 1111
  x=(x&list2)+((x>>1)&list2);//计算2位中的1
  x=(x&list4)+((x>>2)&list4);//计算4位中的1
  x=(x&list8)+((x>>4)&list8);//计算8位中的1
  //现在我们搞定每个字节中的值(8位中的1),接下来我们合并4个字节中的1
  x+=(x>>8);
  x+=(x>>16);
  return x&0x3f;
}

(5) bang

函数功能:逻辑非操作
实现要求:

  • bang - Compute !x without using !
  • Examples: bang(3) = 0, bang(0) = 1
  • Legal ops: ~ & ^ | + << >>
  • Max ops: 12
  • Rating: 4

实现思路:
这个函数实现较为简单,直接利用符号扩充的特点将x取补码,然后与x相或,右移31位+1,如果是一个非0数,得到的结果是-1+1=0,否则为1.
实现代码:

int bang(int x) {
  return (((~x+1)|x)>>31)+1;//利用符号扩充的特点
}

(6) tmin

函数功能:返回二进制最小整形数据
实现要求:

  • tmin - return minimum two’s complement integer
  • 一的补码(one’s complement) 指的是正数=原码,负数=反码
  • 二的补码(two’s complement) 指的就是通常所指的补码
  • Legal ops: ! ~ & ^ | + << >>
  • Max ops: 4
  • Rating: 1

实现思路:
将1右移31位,送至最高位即可
实现代码:

int tmin(void) {//二进制的最小补整数10000000000000000000000000000000,之前一直以为是2的补码
  return 1<<31;
}

(7) fitsBits

函数功能:判断一个整型能否被n位二进制补码表示
实现要求:

  • 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

实现思路:
能否用n位表示,我们可以将x左移32-n然后右移32-n位,将x的符号全部填充为第n位的二进制数,如果说这个结果和x相等,那么说明可以表示,否则不能表示。
实现代码:

int fitsBits(int x, int n) {
  int y;
  n=(~n)+1;
  y=(x<<(32+n))>>(32+n);//得到符号扩展的x
  return !(x^y);
}

(8) divpwr2

函数功能:计算x/(2^n)的值并且向0取整
实现要求:

  • 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

实现思路:
这里实质上以做右移n位的操作,但是需要区分是正数还是负数,如果是正或0直接右移n位,为负数则需要加上偏置量后再进行移位操作。其中偏置为2^n-1.
实现代码:

int divpwr2(int x, int n) {
    int temp=((1<<n)+(~0))&(x>>31);
    return (x+temp)>>n;
}

(9) negate

函数功能:计算负数
实现要求:

  • negate - return -x
  • Example: negate(1) = -1.
  • Legal ops: ! ~ & ^ | + << >>
  • Max ops: 5
  • Rating: 2

实现思路:补码操作,取反加1
实现代码:

int negate(int x) {
  return (~x)+1;
}

(10) isPositive

函数功能:判断一个数是否是正数返回1,否则返回0
实现要求:

  • isPositive - return 1 if x > 0, return 0 otherwise
  • Example: isPositive(-1) = 0.
  • Legal ops: ! ~ & ^ | + << >>
  • Max ops: 8
  • Rating: 3

实现思路:
右移取符号填充,然后要求x非零,y为0
实现代码:

int isPositive(int x) {
  int y=x>>31;
  return (!!x)&(!y);
}

(11) isLessOrEqual

函数功能:判断x<=y,是则返回1否则返回0
实现要求:

  • isLessOrEqual - if x <= y then return 1, else return 0
  • Example: isLessOrEqual(4,5) = 1.
  • Legal ops: ! ~ & ^ | + << >>
  • Max ops: 24
  • Rating: 3

实现思路:
判断x<=y,我们可以做减法。首先计算y-x然后将其右移31位取符号,取y的符号ys,取y^x的值的符号s,然后要求x<=y就必须满足:xy同号并且相减结果为正,或者xy异号,但y为正
实现代码:

int isLessOrEqual(int x, int y) {
  //同号相减,异号y为正
  int sub=(y+(~x)+1)>>31;
  int ys=(y>>31);
  int s=(y^x)>>31;
  return (!s&!sub)|(s&!ys);
}

(12) ilog2

函数功能:计算x对2的对数并且向下取整
实现要求:

  • ilog2 - return floor(log base 2 of x), where x > 0
  • Example: ilog2(16) = 4
  • Legal ops: ! ~ & ^ | + << >>
  • Max ops: 90
  • Rating: 4

实现思路:
采用二分的方法来进行处理,首先选择中间的第16位,得到结果的逻辑值乘以16,然后处理logs+8右移8位乘以8.一次类推直到为1
实现代码:

int ilog2(int x) {
  int logs=(!!(x>>16))<<4;
  logs=logs+((!!(x>>(logs+8)))<<3);
  logs=logs+((!!(x>>(logs+4)))<<2);
  logs=logs+((!!(x>>(logs+2)))<<1);
  logs=logs+(!!(x>>(logs+1)));  
  return logs;
}

(13) float_neg

函数功能:返回一个unsigned表示浮点数的负数
实现要求:

  • 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

实现思路:
浮点数的负数实现很久暗淡,但是需要注意的是对于一个浮点数,他可能是nan,也可能是正负无穷大,也可能是一个可以正常表示的浮点数。对于nan,我们需要直接返回这个值,否则取反符号位。
实现代码:

unsigned float_neg(unsigned uf) {
  unsigned int a=0xff;
  unsigned int b=1<<31;
  unsigned int exp=(uf>>23)&a;//取出阶码
  unsigned int frac=uf&0x7fffff;//去除尾数
  if(!(exp^a)&&frac) return uf;//当nan,返回uf否则返回-uf
  return uf^b;
}

(14) float_i2f

函数功能:返回一个int数据强制转换位浮点数的unsigned类型表示
实现要求:

  • 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

实现思路:
首先我们判断是否为0,如果是那么直接返回。否则计算阶的值exp,如果exp小于等于23,说明可以正常表示,我们只需将尾数左移23-exp即可。如果exp>23,则发生位的截取,这个时候我们要判断是四舍五入还是向偶数取整。我们可以在截取位置加上1。之后我们判断以前的截取位置,如果不为1,我们减去1,如果是四舍五入产生的那么因为减法的借位是不会影响到frac的,如果是向偶数舍入也不会影响。最后我们将exp加上这个尾数,处理有可能产生的向exp进位,然后返回和并值。
实现代码:

unsigned float_i2f(int x) {
if(!x) return 0;//为0返回
    else{
int exp=~0;//阶初始化为-1
    unsigned temp1=x;
    unsigned temp2,temp3;
    int shift;
    int sign=0x80000000&x;
    if(sign) temp1=-temp1;
    temp3=temp2=temp1;
    while(temp1){
        exp=exp+1;
        temp1=temp1>>1;
    }
    shift=23-exp;//计算要移位的长度
    if(shift>=0)  temp3=temp3<<shift;//shift为正,表示可以用23位表示
    else//否则发生截取,考虑舍入
    {
        shift=-shift;
        temp2=temp2>>shift;//右移shift位
        temp3+=(1<<(shift-1));//加上低位的进位
        if(!(temp2&1)) temp3+=-1;//如果最低位不为1,说明无法发生偶数进位,所以减去1。如果四舍五入,则不会借位成功舍入,否则不发生进位
        temp3=temp3>>shift;//右移shift
    }
    exp=((exp+0x7f)<<23)+(temp3>>1);//加上temp3可能发生向exp的进位
    exp=exp&0xff800000; //清零frac
    temp3=temp3&0x7fffff;//取后23位
    return sign|exp|temp3;}//合并
}

(15) float_twice

函数功能:返回一个unsigned表示的浮点数*2的结果
实现要求:

  • 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

实现思路:
这个函数实现并不复杂,我们首先对数据进行分类,如果数据是一个nan或者是无穷大,那么我不能改变它的值,直接返回,否则要判断数据是不是非规格化数,如果是那么将尾数位左移1位,如果是规格化数,我们可以直接将exp+1,但是需要注意的是如果因为exp+1产生了exp=0xff,那么结果因该是无穷大而不是nan,所以这种情况下我们要将frac尾数清零。最后注意合并。
实现代码:

unsigned float_twice(unsigned uf) {
  unsigned int s=uf&0x80000000;
  unsigned int exp=uf&0x7f800000;
  unsigned int frac=uf&0x7fffff;
  if(exp==0x7f800000)//Nan and inf
  {
    return uf;
  }
  else if(!exp)//非规格化数
  {
    frac<<=1;
  }
  else//规格化数,直接exp+1,同时如果exp全1那么需要对frac清零防止nan
  {
    exp+=(1<<23);
    if(exp==0x7f800000) frac=0;
  }
  return (s|exp|frac);
}	

实验结果及分析:

(1) 输入:命令./dlc bits.c和./dlc -e bits.c查看语法错误并检测操作符消耗:
在这里插入图片描述
(2)编译并执行程序:输入命令make btest编译,指令./btest执行:
在这里插入图片描述

结果分析:
编译之后,我们的15个函数的操作符资源消耗分别为4,3,7,35,5,1,8,7,2,5,13,27,8,27,11满足程序的设计要求,并且通过编译,无警告和错误。运行之后各项得分分别为1,2,3,4,4,1,2,2,2,3,3,4,2,4,4。最后得分为41.
程序设计满足实验要求。

收获与体会:
本次实验实际上我从上周就开始着手做了,但是因为我电脑正在维修,用的是平板修改bits.c文件,进度很慢,然后请同学帮我运行这段代码。其中我感触颇深的一点是思考是无处不在的,哪怕没有实验环境也要求我们能够全方位的深入到实验之中,另外我似乎感受到了以后工作中共同协作,封装一个API交给测试,等候测试的反馈这种欣喜却又急躁的感觉。除此之外,本次实验实际上是充满挑战性的,关于位操作,我一开始还不是太熟悉,每一题都要想很长一段时间,特别是计算数据中有多少二进制位为1,计算log值,和浮点数强制转换。其中我学到了一种全新的二分法,利用既定的二进制数据,一次次与x进行相与,最后求和。数据的右移位是会填充的,负数会全部填充为1,而不是简单的逻辑位移,如果想要逻辑位移一个负数我们要加上偏移量。我重新认识了异或操作的实用性,在取反数据,比较数据时异或往往发挥很大的作用。

原创文章 236 获赞 430 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_44307065/article/details/105497452
今日推荐