ics计算机系统全称,ICS计算机系统实验--datalab实验

实验内容及操作步骤:

一.代码完善及代码思路说明

/*

* 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)));

}

代码思路:

根据德摩根律,我们可以得出两个数的相与等于其的反相或再取反,即:a&b=~(~a|~b),   取反运算符和相或运算符都已给出因此可用此解法。

/*

* 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位二进制的数中截取一个8位二进制的一个字节,因此我    们可以把        想要截取的字节移动到32位二进制的最低八位然后和0xff相与就可以将前24位清 零只        保留最后的八位。至于怎么将想要的8位二进制移动到最后八位,可以利用n的倍数关 系移位将其变为8的不同倍数,然后再对原32位数通过移位操作就能执行。

/*

* 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 ~(((0x1<<31)>>n)<<1)&(x>>n);

}

代码思路:

因为函数传入的x是int型所以在直接用>>进行右移操作时会默认进行算术右移,按符号位进    行拓展,而题目要求我们实现的是逻辑右移。

很自然的可以想到将算术右移后多余的符号位和0相与清零,而要保留的数据部分和1 相与        不变,这样就能实现逻辑右移的操作。

因此对于一个想实现逻辑右移n位的数,我们需要构造一个前n位都是0后面的32-n位都是       1的二进制数来和它相与清除符号位。首先将0x1左移31位得到符号位是1其他位是0的一       个二进制数,因为可用操作符中没有提供减号,所以通过右移n位再左移1位,来实现使这 个二进制数的前n位都是1。然后对这个数取反,就得到了一个前n位都是0后面的32-n位   都是1的二进制数,将它和进行了算术右移的x相与就能清空x前面的n位符号拓展的位, 而保留数据位。实现逻辑右移操作

/*

* 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 tmp=((0x01<<8|0x01)<<8|0x01)<<8|0x01;

int val=x&tmp;

val+=tmp&(x>>1);

val+=tmp&(x>>2);

val+=tmp&(x>>3);

val+=tmp&(x>>4);

val+=tmp&(x>>5);

val+=tmp&(x>>6);

val+=tmp&(x>>7);

val=val+(val>>16);

val=val+(val>>8);

return val&0xff;

}

代码思路:

函数要求出一个int型里一共有几个1,我们可以先构造一个32位二进制        00000001000000010000000100000001,然后让它和x相与就能得到第1,9,17,25位的1的个数,   然后x右移1位就是第2,10,18,26位的1的个数和之前的值相加。这样不断右移最终就能遍历    整个二进制数,这时1的个数分四段保存在32位中,然后折半相加,最终的八位里存储的就        是1的全部数量。

/*

* bang - Compute !x without using !

*   Examples: bang(3) = 0, bang(0) = 1

*   Legal ops: ~ & ^ | + << >>

*   Max ops: 12

*   Rating: 4

*/

int bang(int x) {

return (~((x|(~x+1))>>31))&0x1;

}

代码思路:

函数要求的是实现逻辑运算!,即当一个数为0时返回1,不为0时返回0,因此实际上只需 要判断所给的数是不是等于0即可。

首先想到的应该是x和他自身的关系即和~x相与相或等操作,但是发现并没有什么特例性可        以用来实现这个函数,但是查阅资料发现只有当x为0是他取反加一(~x+1)和x相或后最        高位为0,因为只有x为0时,~x+1为2的32次方会产生溢出,此时x和~x+1的最高符号    位都是0,所以二者相或后也是0。而除了0之外的任何数他们二者符号位相或都是1,可以 利用这个特性。将最高位右移到最低位取反然后和1相与就能实现函数功能。当x为0时返 回1,当x不为0时返回0.

/*

* tmin - return minimum two's complement integer

*   Legal ops: ! ~ & ^ | + << >>

*   Max ops: 4

*   Rating: 1

*/

int tmin(void) {

return (0x1<<31);

}

代码思路:

题目要求的是给出32位2进制补码能表示的最小的整数,根据有符号数的补码的解释规则,        根据定义就能得到,最小的整数的补码表示是10000…0,除了第一位符号位是1,后31位全       为0,因此直接将0x1左移31位得到。

/*

* 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) {

int tmp = x >> (n+(~0x1+1));

return ( !tmp | !(tmp+1)  );

}

代码思路:

经过多组的数据测试,我们发现对于一个正数如果想要被n位的二进制准确表示,那么他的        32位二进制表示从第n位到第32位都应该是0,因为截取后第n位就是其符号位,同理,对      于一个负数他的32位二进制表示从第n位到第32位都应该是1,这样的数才能被准确表示。

因此我们只需要把一个数x的32位形式右移n-1位,只保留第n到第32位如果全为0或1    就能被准确表示,反之不能。

因为可用操作符中没有给出减号 ,所以用0x得到补码形式的-1 和n相加得到n-1,然后通  过表达式(!tmp | !(tmp+1))可以判断前面的字符是否为全0或全1,如果不是返回0,是的话      返回1

/*

* 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) {

int sign = (x>>31);

int temp = (0x1<

return (x+(sign&temp))>>n;

}

代码思路:

这个函数要实现的就是二进制的除法,二进制除以2的幂,我们在课本中已经学到过,二进        制除以2的幂可以通过右移来实现,但是对于有符号数,正数和负数的算法是不同的的,直        接右移进行的是向下取整,而我们要的是向0取整,因此在对负数的计算是需要加一个偏量 2^n-1。

算法设计,首先我们可以先判断x是否为正数,把x算术右移31位把他的符号位拓展32位,

偏量2^n-1可以直接用1左移然后和0的取反相加得到,将这两个数相与当x为正数时,符号    位全是0,相与为0则不加偏量,当为负数时,符号全为0,和偏量相与得到的还是偏量加给 x,然后再进行右移操作得到的商就都是向零取整的了。

/*

* negate - return -x

*   Example: negate(1) = -1.

*   Legal ops: ! ~ & ^ | + << >>

*   Max ops: 5

*   Rating: 2

*/

int negate(int x) {

return ((~x)+1);

}

代码思路:

这个题是求相反数的补码表示法,是在数据的补码表示中学过的知识点,我们可以用取反加        一的方法得到一个数相反数的补码表示。

/*

* isPositive - return 1 if x > 0, return 0 otherwise

*   Example: isPositive(-1) = 0.

*   Legal ops: ! ~ & ^ | + << >>

*   Max ops: 8

*   Rating: 3

*/

int isPositive(int x) {

int  temp=x>>31;

return !( temp | !x );

}

代码思路:

函数要求判断一个数x是否为正数,如果不是则返回0,因此当x为负数或者是0的情况就返 回0,我们可以通过符号位判断正负将x算术右移31位得到32位的符号位,通过!x来判断      x是不是0,当符号位是1或者说!x是1,即x是负数或者是0的时候返回0.实现函数。其  他时候也就是大于零的时候返回1.

/*

* 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) &0x1;

int signy = (y >> 31) &0x1;

int isSameSign= ! (signx ^ signy) ;

int p=! ( ( (~x)+1+y ) >> 31);

int ans = ( isSameSign & p ) | ( ( !isSameSign ) & signx);

return ans ;

}

代码思路:

很容易想到通过判断y-x >= 0来判断x和y的大小关系,但是这样就会出现y-x溢出的情况,     为了防止这种情况发生,我们可以将这个问题分为同号和异号两种情况。同号:我们就通过        判断y-x的符号是不是=0的情况! ( ( (~x)+1+y ) >> 31)。如果是异号:我们就直接判断x的符        号是不是-即可。这样就能避免溢出而出现的不准确的错误。

/*

* 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 count = 0;

count = (!!(x>>16)) << 4;

count =count+((!!(x>>(8 + count)))<<3);

count =count+((!!(x>>(4 + count)))<<2);

count =count+((!!(x>>(2 + count)))<<1);

count =count+((!!(x>>(1 + count)))<<0);

return count;

}

代码思想:

这个函数的补全方法是利用二分的思想,根据公式log(x)=16a+8b+4c+2d+e。那么   count=abcde就是log(x)的二进制表示。因为x长32位,首先我们先将x>>16,  判断高16       位是不是大于0,如果>0,!(x>>16)就是0,我们要将他转换到a的位置就是将! !(x>>16)再次        取非得到1,然后<<4,到a的位置,它的权是16,就说明这个数大于16,1肯定在高16位   处,然后在接着将高位折半到8位,就是>>(8+16),看看高8位是不是也是>0。这样一步步    的分下去就是答案。最终得到的count就是log(x)。

/*

* 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 x=0x80000000;

unsigned wu=0xFF000000;

unsigned tmp = uf<<1;

if( (wu&tmp ) ==  wu)

{

if(tmp != wu) return uf;

}

return uf^x;

}

代码思路:

函数要实现的功能就是返回输入的uf的负数形式-uf。如果uf是NAN形式就直接返回参数。        由ieee浮点数的编码规则可知,浮点数是由s符号位(1位)E阶码(8位)M尾数(23位)(-1)^s * M   * 2^E       组成的,当E是0xFF时,要么这个数是inf或NAN,可inf也很特殊就是M就是 全0的情况所以我们捉住这个特殊点,先把uf<<1忽略了s符号位,就看看0xFF000000在      uf<<1的位置是不是也是0xFF000000,如果是再判断一下uf是不是就等于0xFF000000就    代表他原先就是个inf数,如果不等于0xFF000000就代表他原先就是个NAN数。按情况和    0x80000000异或就可以实现符号位反转。

/*

* 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 sign=0,shiftleft=0,flag=0,tmp;

unsigned absx=x;

if( x==0 ) return 0;

if( x<0 ){

sign=0x80000000;

absx=-x;

}

while(1){

tmp=absx;

absx<<=1;

shiftleft++;

if( tmp&0x80000000 ) break;

}

if( (absx & 0x01ff) > 0x0100 ) flag=1;

if( (absx & 0x03ff) == 0x0300 ) flag=1;

return sign+(absx>>9)+((159-shiftleft)<<23)+flag;

}

代码思路:

题目要求是把一个int型转化成float型表示,首先要用一个符号位储存符号,因为int的正负      数表示方法和float不同,所以先把负数从补码形式转化成方便float表达的形式,然后获取到       最高位的1,和阶码,这其中需要注意的就是在保留23位的时候需要进行向偶数舍入考虑是  否进位,最后把几个部分相加就能得到转化成的float型。感觉这道题目比较难。

/*

* 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) {

int s = uf & 0x80000000;

int e = uf & 0x7f800000;

int m = uf & 0x007fffff;

int ans = 0;

if (e == 0x7f800000) return uf;

if (e == 0) ans = s | (uf << 1);

else  ans = s + e + 0x00800000 + m;

return ans;

}

代码思路:

本函数要求实现的的是浮点数乘2,对其进行处理我们可以先把浮点数拆成s(1位)E(8位)M(23       位)三个部分,首先,考虑特殊值的情况,不管是NAN还是无穷的情况返回的值都是他本身,      所以只要阶码部分全为1,就返回参数本身。对于非规格化数:我们只需要将其整体左移一位        并和符号位相或就可以。对于规格化数乘2也就是阶码加一,将阶码加一后再把整个数组合        起来就可以了。

实验结果及分析:

对补充完整后的代码进行检查,符合要求,代码合法:

39cd718978438853079283ba3f5dee27.png

对结果进行检验,所有样例均通过:

59b1431159ab7d207592452cd3212dd1.png

收获与体会:

通过本次实验,感觉这次实验对我们对数据的存储和表达方式的要求比较高,   有很多题都比较难,很难想到,尤其是后面对浮点数进行操作的几个函数,必须 十分灵活的运用移位操作,熟悉位之间的转换。

这次实验不仅要求我们掌握好知识点,还要求我们的思路必须打开,要能灵活   的应用,真正从底层上加深了对数据的存储和表示的理解,虽然有些函数自己真 的很难写出来,只有在参考了网上的资料后才能写得出来。但是相信经过了这次 实验,对于数据表示和存储能有更深的理解。

标签:tmp,右移,return,ops,--,32,int,实验,ICS

来源: https://blog.csdn.net/weixin_44668030/article/details/110492017

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值