计算机系统_数据表示实验

(1)实验环境

        Linux操作系统

(2)实验内容

    ①根据bits.c中的要求补全以下的函数:
        int bitXor(int x, int y);
        int tmin(void);
        int isTmax(int x);
        nt allOddBits(int x);
        int negate(int x);
        int isAsciiDigit(int x);
        int conditional(int x, int y, int z);
        int isLessOrEqual(int x, int y);
        int logicalNeg(int x);
        int howManyBits(int x);
        unsigned float_twice(unsigned uf);
        unsigned float_i2f(int x);

        int float_f2i(unsigned uf);


    ②在Linux下测试以上函数是否正确,指令如下:
        *编译:./dlc bits.c
        *测试:make btest

        ./btest

    ③具体

本次为一次计算机系统实验,就是使用一些基本的运算符来实现函数功能。

//1

/* 
 * bitXor - 仅允许使用~和&来实现异或 
 *   例子: bitXor(4, 5) = 1
 *   允许的操作符: ~ &
 *   最多操作符数目: 14
 *   分值: 1
 */


/* 
 * tmin - 返回最小的二进制补码 
 *   允许的操作符: ! ~ & ^ | + << >>
 *   最多操作符数目: 4
 *   分值: 1
 */


//2
/*
 * isTmax - 如果x是最大的二进制补码,返回1;否则,返回0
 *   允许的操作符: ! ~ & ^ | +
 *   最多操作符数目: 10
 *   分值: 2
 */
/* 
 * allOddBits - 如果所有奇数位都为1则返回1;否则返回0
 *   例子: allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
 *   允许的操作符: ! ~ & ^ | + << >>
 *   最多操作符数目: 12
 *   分值: 2
 */


/* 
 * negate - 返回-x 
 *   例子: negate(1) = -1.
 *   允许的操作符: ! ~ & ^ | + << >>
 *   最多操作符数目: 5
 *   分值: 2
 */


//3
/* 
 * isAsciiDigit - 如果x是ascii码中的0~9,返回1;否则返回0
 *   例子: isAsciiDigit(0x35) = 1.
 *            isAsciiDigit(0x3a) = 0.
 *            isAsciiDigit(0x05) = 0.
 *   允许的操作符: ! ~ & ^ | + << >>
 *   最多操作符数目: 15
 *   分值: 3


/* 
 * conditional - 实现x?y:z 
 *   例子: conditional(2,4,5) = 4
 *   允许的操作符: ! ~ & ^ | + << >>
 *   最多操作符数目: 16
 *   分值: 3
 */


/* 
 * isLessOrEqual - 如果x<=y返回1否则返回0 
 *   例子: isLessOrEqual(4,5) = 1.
 *   允许的操作符: ! ~ & ^ | + << >>
 *   最多操作符数目: 24
 *   分值: 3
 */
//4
/* 
 * logicalNeg - 实现!运算符的功能
 *   例子: logicalNeg(3) = 0, logicalNeg(0) = 1
 *   允许的操作符: ~ & ^ | + << >>
 *   最多操作符数目: 12
 *   分值: 4 


/* howManyBits - 返回将X表示为补码所需的最小有效位数。
 *  例子: howManyBits(12) = 5
 *        howManyBits(298) = 10
 *        howManyBits(-5) = 4
 *        howManyBits(0)  = 1
 *        howManyBits(-1) = 1
 *        howManyBits(0x80000000) = 32
 *  允许的操作符: ! ~ & ^ | + << >>
 *  最多操作符数目: 90
 *  分值: 4
 */


//float
/* 
 * float_twice - 以unsinged表示的浮点数二进制的二倍的二进制unsigned型
 *   参数和结果都会被作为unsigned返回,但是会表示为二进制的单精度浮点值。
 *   允许的操作符: 任何整数或者无符号数操作符包括: ||, &&. also if, while
 *   最多操作符数目: 30
 *   分值: 4
 */


/* 
 * float_i2f - 返回int x的unsigned浮点数的二进制形式
 *   参数和结果都会被作为unsigned返回,但是会表示为二进制的单精度浮点值
 *   允许的操作符: 任何整数或者无符号数操作符包括: ||, &&. also if, while
 *   最多操作符数目: 30
 *   分值: 4
 */


/* 
 * float_f2i - 返回unsigned uf的整型数的二进制形式
 *   参数和结果都会被作为unsigned返回,但是会表示为二进制的单精度浮点值
 *   任何超过范围的数都应该返回 0x80000000u.
 *   允许的操作符: 任何整数或者无符号数操作符包括: ||, &&. also if, while
 *   最多操作符数目: 30
 *   分值: 4
 */

(3)实验步骤

1. int bitXor(int x, int y);

由离散数学知识可得到异或:

int bitXor (int x, int y) {
   return ~(~(~x&y)&~(x&~y));
}

2. int tmin(void);

最小值为0x8000 0000,我们可以将1左移31位得到最小值:

int tmin(void) {
   return 1<<31;
}

3. int isTmax(int x);

这里我参考了一个师兄的方法,再加上我的方法,所以列出了两种方法:

①由于最大值是0x7fff ffff,0x7fff ffff+1->0x1000 0000,而对0x1000 0000来说,0x1000 0000+0x1000 0000=0;而自身相加为零的就只有0和0x1000 0000,然后再将0xffff ffff(-1)排除即可。

#####################################补充####################################

32位的int,正数的范围是(0,0x7FFFFFFF),负数(0x80000000,0xFFFFFFFF)

负数 = -(0x100000000 - (0x80000000,0xFFFFFFFF))

0xFFFFFFFF实际代表的值是 -1

############################################################################

int isTmax(int x) {
   return !(~(x^(x+1)))&!!(x+1);
}

②我们已经在2中写出了tmin,这里可以直接用tmin求得tmax再~验证:

int isTmax(int x) {
   return !(x^~(1<<31));
}

4. int allOddBits(int x);

判断奇数位是否全为1,则需要先把偶数位置1(通过与0x5555 5555进行或运算),再按位取反,观察若值为0则应返回1。在二进制中,只有所有奇数位都为1的数与0x5555 5555进行&运算才会得到0,由于实验要求在32位机器上操作,所以不能直接使用0x5555 5555操作,所以问题转变为如何求得0x5555 5555。要得到0x5555 5555可以将0x55分别左移8,16,24位,得到三个数:

0x55 = 0x50 + 0x5 = 5 + (5<<4)

0x55->(0101 0101)₂;

0x555->(0101 0101 0101)₂;

0x55 5555->(0101 0101 0101 0101 0101 0101)₂;

0x5555 5555->(‭0101 0101 0101 0101 0101 0101 0101 0101‬)₂;

HEX 55 = DEC 85 = BIN 0101 0101

0x5555 5555 = 85+(85<<8)+(85<<16)+(85<<24)

------->

从HEX值可以看出,这就是由0x55左移相加后得到的0x5555 5555。

int allOddBits(int x) {
   return !(~(x|(85+(85<<8)+(85<<16)+(85<<24))));
}

5. int negate(int x);

取反加一就OK!

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

6. int isAsciiDigit(int x);

若x是数字,则x在‘0’~‘9’之间。可以用x-48>=0和x-58<0(x+~48+1>=0和x+~58+1<0)来计算

int isAsciiDigit(int x) {
  return !((x+~48+1)>>31)&!!((x+~58+1)>>31);
}

7. int conditional(int x, int y, int z);

首先使t=!x,当x为0时返回1,当x不为0时返回0,根据题意得到( &y)|( &z);

HEX 0xffff ffff = DEC (-1)

HEX 0x0 = DEC (0)

首先对空①,当x不为0即t=0时,需要将t转换为0xffff ffff,当x=0即t=1时,需要将t转换为0x0,直接t-1即可得到0,所以空①为“!x+~1+1”,同理空②为“~!x+1”:

int conditional(int x, int y, int z) {
   return ((!x+~1+1)&y)|((~!x+1)&z);
}

8. int isLessOrEqual(int x, int y);

判断x是否小于等于y,直接用y-x可能会超出int的表示范围,故而:

①在x与y同号的情况下转换为p=y-x>=0,然后对p符号位进行(p>>31)&1操作,符号位为0则返回1,符号位1则返回0;

②x,y异号时,只要x>=0,就要返回0,否则返回1,由(x>>31)&1能达到该效果;

③c=a+b可作为x,y同号异号的判断。

int isLessOrEqual_1(int x, int y) {
   int a = x>>31;
   int b = y>>31;
   int c = a+b;
   int p = !((~x+1+y)>>31);
   return (c&(a&1))|((~c)&p);
}
或者考虑按符号来分情况讨论:

①x为负,y为正;②x和y同号,相等;③x+~y+1<0。

int isLessOrEqual_2(int x, int y) {
	int xs = ((x>>31)&1);
	int ys = ((y>>31)&1);
	return (xs&!ys)|(!(xs^ys)&(!(x^y)|(x+~y+1>>31)&1));
}

9. int logicalNeg(int x);

令y=~x+1,考虑x与y的符号位:

①当x=0x0时,x,y两者符号位都为0;②当x=0x8000 0000时,x,y两者符号位都为1;

③否则,两者符号位为01或10;④根据离散数学的真值表得出(~x)&(~y),则当且仅当x=0x0时,其符号位为1。

int logicalNeg_1(int x) {
   return ((~(~x+1)&~x)>>31)&1;
}

或者这样考虑,直接用x<0 && x>=1转换

int logicalNeg_2(int x) {
   return ((x>>31)+1)&((x+~1+1>>31)&1);
}

10. int howManyBits(int x);

求其表示的最小位数。如果x是负数,那么对x取反,因为需要的位数是一样的;然后采用2分法,就是通过右移一半位数来判断,先对其右移16bit,若为0,则最低数量为16,根据此思想,继续对其右移8bit、4bit、2bit、1bit得到h16,h8,h4,h2,h1,分别表示一分为二后其中的一半是否存在,而另一半继续循环直至1,这时还剩下x,最后还需要再加上符号位0。

int howManyBits_1(int x) {
	x = ((x>>31)&~x|(~!(x>>31)+1)&x);
	int h16,h8,h4,h2,h1,y;
	
	y = x>>16;
	h16 = !!y<<4;
	x = x>>h16;
	
	y = x>>8;
	h8 = !!y<<3;
	x = x>>h8;
	
	y = x>>4;
	h4 = !!y<<2;
	x = x>>h4;
	
	y = x>>2;
	h2 = !!y<<1;
	x = x>>h2;
	
	y = x>>1;
	h1 = !!y<<0;
	x = x>>h1;
	
	return h16+h8+h4+h2+h1+1+x;
}

思路一样的另一种方法
int howManyBits_2(int x) {
	int s, s1, s2, s4, s8, s16;
	int t1 = ((!x)<<31)>>31;
	int t2 = ((!~x)<<31)>>31;
	int op = x^(x>>31);
	s16 = (!!(op>>16))<<4;
	op = op>>s16;
	s8 = (!!(op>>8))<<3;
	op = op>>s8;
	s4 = (!!(op>>4))<<2;
	op = op>>s4;
	s2 = (!!(op>>2))<<1;
	op = op>>s2;
	s1 = (!!(op>>1));
	op = op>>s1;
	s = s1+s2+s4+s8+s16+2;
	return (t2&1)|((~t2)&((t1&1)|((~t1)&s)));
}

11. float_twice

求float的2的倍数。将uf(unsigned float)切割成符号位s,阶码exp和位数frac,分情况讨论

①如果exp==0xff,continue;②如果exp==0,那么直接frac<<=1;③否则exp递增,然后再判断一下杜绝null。

unsigned float_twice(unsigned uf) {
        unsigned s = uf&0x80000000;
	unsigned exp = uf&0x7f800000;
	unsigned frac = uf&0x007fffff;
	if(!exp) {
		frac<<=1;
	}else if(exp^0x7f800000) {
		exp += 0x00800000;
		if(!(exp^0x7f800000)) {
			frac = 0;
		}
	}
	return s|exp|frac;
}

或者这样考虑:①对于0,返回0;②对于指数全为1,由于溢出,返回的是自身;③对于指数为0,位数左移一位。当其最高为1,需要指数加1;④指数+1,此时指数全为1,尾数为0。

unsigned float_twice(unsigned uf) {
	int exp = 0x7f800000&uf;
	int num = 0x007FFFFF&uf;
	unsigned flag = 0x80000000&uf;
	int n = (exp>>23)-127;
	if(uf == 0x0) 
		return 0;
	if(n == 128) 
		return uf;
	if(exp == 0) {
		if(uf&0x00400000 {
			num = num<<1;
			exp = 0x00800000;
		}
		else 
			num = num<<1;
    }
    else {
		exp = exp+0x00800000;
		n = (exp>>23)-127;
		if(n == 128)
			num=0;
    }
	return (exp|num|flag);
}

12. unsigned float_i2f(int x);

(1)首先我们需要知道int型的范围为-2^31~2^31-1主要有两种情况:
①用(二进制)科学计数法表示int型数时,尾数位数<=23,例如0x00008001,此时将0x8001左移24-16=8位得到frac,而exp则127+16-1;
②当尾数位数>23时,找到位数最末一位记作x[i],然后对尾数的舍去分3种情况考虑,初始化c=0:
 a)当x[i-1]=1且x[i-2]、x[i-3]…x[0]都为0(即要偶端舍入情况),且x[i]=1,令c=1(此处frac若是全为1,则会导致frac+c超出范围,这是bug,当还是通过了);
 b)当x[i-1]=1且x[i-2]、x[i-3]…x[0]不都为0,令c=1(与a存在同样的bug);
 c)除a、b的情况,c=0;

③其他特殊情况再考虑一下就好了;

以0x0800 8001为例子
31               <-----               0
0000 1000 0000 0000 1000 0000 0000 0001
下划线部分为保留下来的frac,最后一位的下标记为i,在这里i=8.
unsigned float_i2f_1(int x) {
	unsigned abs = x;
	unsigneds = 0x80000000&x;
	unsignedtem = 0x40000000;
	unsignedexp_sign = 0;
	unsigned exp = 0;
	unsigned frac;
	unsigned c = 0;
	
	if(x == 0) {
		return x;
	}
	else if(x == 0x80000000) {
		return (s+(158<<23));
	}
	else {
		if(x<0) {
		   abs = -x;
		}
        while(1) {
			if(tem&abs)
				break;
			tem = tem>>1;
			//printf("%x\n%x\n",tem,abs);
			exp_sign = exp_sign+1;
		}//while
		
		//printf("exp_sign = %d\nx = %x\n",exp_sign,x);
		
		frac = (tem-1)&abs;
		//printf("frac = %x\n",frac);
		
		if(31-1-exp_sign>23) {
			//尾数大于23位的情况
			int i = 30-exp_sign-23;
			if((frac<<(31-(i-1))) == 0x80000000){
				//ou = quzhi
				if((frac&(1<<i))!= 0)
					c = 1; 
			}
		}
		else if((frac&(1<<(i-1)))!= 0) {
			c = 1; 
		}	
		frac = frac>>i;
		}
		else
			frac = frac<<(23-(31-exp_sign-1));
			
		exp = 157-exp_sign;
		//printf("exp = %d\n s = %x\n",exp,s);
		//printf("frac = %x\n",frac);
		//printf("result = %x\n",(s+(exp<<23)+frac+c));
		return (s+(exp<<23)+frac+c);
	}//else 
}

(2)具体解释见代码注释:

unsigned float_i2f_2(int x) {  
    int sign = x>>31&1;//获取符号位(1位)  
    int i;  
    int exponent; //指数域(8位)  
    int fraction; //小数域  
    int delta;//偏差(用于舍入)  
    int fraction_mask;//小数域的掩码(23位)  
    if(x == 0)//如果为0就直接返回  
        return x;  
    else if(x==0x80000000)//如果为TMin,解释为-(2^31),对应float就是(1)x1.0x2^31,所以S=1,M=1,E=31,指数域=31+127=158  
        exponent=158;  
    else{  
        if (sign)//通过前面的操作已经确定了符号,先把int的绝对值获取,之后利用浮点数的计算公式即可计算出float的值,现在要获取绝对值存入内存中  
            x = -x;  
          
        i = 30;//最高位是符号位,次高位是有效数字的起始位  
        while ( !(x >> i) )//从左往右查找有效数字第一个不为零的位,对应的位置就是最终的i(这里的位置从0开始标号)  
            i--;  
        //printf("%x %d\n",x,i);  
        exponent = i + 127;//数值的最高位已经找到是第i位(有效数据共有i+1位),又因为int类型不可能是非规格数据的范围(为0的情况在前面已排除),所以小数域就是d第i为后面的位向量(小数域一共有i个位),故阶码E=i(小数部分x2的E次方),指数域等于i+127;  
        x = x << (31 - i);//清除有效数据前面的所有0,包括符号位,得到有效数据开头的数据  
        fraction_mask = 0x7fffff;//设置23位的小数域掩码  
        fraction = fraction_mask & (x >> 8);
		
		/**
		* 虽然按照浮点数格式,最前面的9个位不加入小数位,按道理应该右移9位,  
		* 但是由于int类型的参数不可能是非规格数,所以最前面的一个有效数据也被舍弃(默认M=1+f),  
		* 当向右移动8位,舍弃了有效数据的低8位,再和掩码处理以后,一共舍弃了9位  
		* 除了低八位还包括有效数据的最高位,类比二进制小数中小数点左边的那一位数字,在float存储的时候,小数点左边数字不存入内存  
		* 用9个位用来存储符号位+指数域  
		*/
		
        x = x & 0xff;//由于右移8位,舍弃了有效数据第八位,现在获取低八位用于舍入操作  
        delta = x > 128 || ((x == 128) && (fraction & 1));
		
		/**
		* 如果低八位超过八位二进制能表示的无符号数的一半,  
		* 要在小数域+1,普通的四舍五入思想  
		* 如果低八位刚好等于八位二进制能表示的无符号数的一半,而且小数域目前最后一位是1,  
		* 根据向偶数舍入的模式,也要在小数域+1,向上舍入的思想,  
		* 如果低八位刚好等于八位二进制能表示的无符号数的一半,如果小数域目前最后一位是0,则向下舍入,不加1  
		* 如果低八位刚好小于八位二进制能表示的无符号数的一半,直接丢弃,不加1,普通的四舍五入思想  
		*/
		
        fraction += delta;//进行舍入  
        if(fraction >> 23) {//如果舍入过后,小数域多余23位,则只取低23位,高位舍弃,但是阶码E要加1,所以指数域也就要加1  
            fraction &= fraction_mask;  
            exponent += 1;  
        }  
    }  
    return (sign<<31)|(exponent<<23)|fraction;//符号位最高位(31),指数域(30--23),小数域(22-0)  
}  

(3)先考虑0和tmin这2个不能没有相反数的情况,然后分离出符号位s和转换成绝对值,计算出x的位数,求出阶码exp,将其移到最左边再右移8位,用多出的8位和第9位(即最后1位)以及相关规则判断是否舍入。

unsigned float_i2f_3(int x) {
	if(!x){
		return x;
	}else if(x == 0x80000000){
		return 0xcf000000;
	}else{
		int s = x>>31&1;
		if(s){
			x = -x;
		}
		
		int i = 30;
		while(!(x>>i)){
			i--;
		}
		int exp = i+127;
		
		x = x<<(31-i);
		int frac = (x>>8)&0x7fffff;
		x = x&0xff;
		int dalta = x>128||((x == 128)&&(frac&1));
		frac += dalta;
		if(frac>>23){
			frac& = 0x7fffff;
			exp++;
		}
		
		return (s<<31)|(exp<<23)|frac;
	}
}

13. float_f2i

返回unsigned uf的整型数的二进制形式,将uf(unsigned float)切割成符号位s,阶码exp和位数frac,分情况讨论:

①当exp=0或exp-127<0时,返回0;
②当exp-127>=31时候,超出表示范围,于是返回0x80000000u;

③当exp-127<=23,根据符号位返回值num>>(23-(exp-127))。

int float_f2i_1(unsigned uf) {
	int s = uf>>31;
	int exp = (uf>>23)&0xff;
	int frac = uf&0x007fffff;
	if(exp<0x7f){
		return 0;
	}else if(exp>157){
		return 0x80000000;
	}else{
		int abs;
		if(exp-150>0){
			abs = 0x00800000+frac<<exp-150;
		}else{
			abs = 0x00800000+frac>>150-exp;
		}
		if(s){
			return -abs;
		}else{
			return abs;
		}
	}
}
exp为0,x=(-1)^s*0.frac*2^(-126);否则x=(-1)^s*1.frac*2^(exp-127)分情况考虑:
①根据float转为int是向0舍入的情况,当exp=0或者exp<127(由exp-127<0得到);
②令exp_sign=((exp>>23)-127)>=0,根据x=(-1)^s*1.frac*(exp-127),
a)exp_sign=0,x=1.x[22]x[21]…x[0]-->x=1;
b)exp_sign=1,x=1x[22].x[21]…x[0]-->x=1x[22];
c)exp_sign=2,x=1x[22]x[21].x[20]…x[0]-->x=1x[22]x[21];
d)exp_sign=23,x=1x[22]x[21]x[20]…x[0]-->x=1x[22]x[21]…x[0];
e)exp_sign=30,x=1x[22]x[21]x[20]…x[0]-->x=1x[22]x[21]…x[0]0…0(共31位);
f)exp_sign=32,x=1x[22]x[21]x[20]…x[0]-->x=1x[22]x[21]…x[0]0…0(共32位),此时,因为int为有符号,只有0x0000 00000能被表示出来,其他都是null;

③当exp_sign=((exp>>23)-127)>=33,null。

int float_f2i_2(unsigned uf) {
	int exp = 0x7f800000&uf;
	unsigned s = 0x80000000&uf;
	int frac = 0x007fffff&uf;
	int frac2 = frac+0x008fffff;
	int exp_sign = ((exp>>23)-127);
	//printf("uf=%x\n",uf);
	if(exp == 0x7f800000)
		return 0x80000000u;
	else if(exp == 0)
		return 0;
	else if(exp_sign <= -1 && exp_sign >= -126)
		return 0;
	else if(exp == 31 && frac == 0 && s != 0)
		return 0x80000000;
	else if(exp_sign >= 31)
		return 0x80000000u;
	elseif(exp_sign <= 23)
		frac2 = frac2 >> (23-exp);
	else
		frac2 = frac2<<(exp-23);
	if(s == 0)
		return frac2;
	else
		return -frac2;
}

最终运行结果:



猜你喜欢

转载自blog.csdn.net/xindolia_ring/article/details/79949335