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<<n)+(~0x0);
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也就是阶码加一,将阶码加一后再把整个数组合 起来就可以了。
实验结果及分析:
对补充完整后的代码进行检查,符合要求,代码合法:
对结果进行检验,所有样例均通过:
收获与体会:
通过本次实验,感觉这次实验对我们对数据的存储和表达方式的要求比较高, 有很多题都比较难,很难想到,尤其是后面对浮点数进行操作的几个函数,必须 十分灵活的运用移位操作,熟悉位之间的转换。
这次实验不仅要求我们掌握好知识点,还要求我们的思路必须打开,要能灵活 的应用,真正从底层上加深了对数据的存储和表示的理解,虽然有些函数自己真 的很难写出来,只有在参考了网上的资料后才能写得出来。但是相信经过了这次 实验,对于数据表示和存储能有更深的理解。
标签:tmp,右移,return,ops,--,32,int,实验,ICS 来源: https://blog.csdn.net/weixin_44668030/article/details/110492017