DataLab实验记录

参考

一、实验目的

本实验目的是加强学生对位级运算的理解及熟练使用的能力。

二、报告要求

本报告要求学生把实验中实现的所有函数逐一进行分析说明,写出实现的依据,也就是推理过程,可以是一个简单的数学证明,也可以是代码分析,根据实现中你的想法不同而异。

三、函数分析

表一

bitXor函数

函数要求:

函数名bitXor
参数int , int
功能实现x^y
要求只能使用 ~&

实现分析:

x^y = (~x&y)|(x&~y)

由于只能使用 ~&,所以

x^y = ~(~(x^y))

= ~(~((~x&y)|(x&~y)))

= ~(~(~x&y)&(~(x&~y)))

函数实现:

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

函数要求:

函数名getByte
参数int , int
功能实现从字x中取出第n个字节
要求! ~ & ^ | + << >>

实现分析:

已知按位与中1和任何数相与(1或0)等于这个数本身,0和任何数相与(1或0)等与0,利用这个原理,可以将给定字x右移适当位数后与0xff相与,从而提取出指定字节。

比如 x = 0x12345678n=1

y = x >> (1<<n) = x >> 2 = 0x??123456;

Byte_n = y&0xff = 0x56

函数实现:

int getByte(int x, int n) {
  return (x>>(n<<3))&0xFF;
}
logicalShift函数

函数要求:

函数名logicalShift
参数int , int
功能实现逻辑右移
要求! ~ & ^ | + << >>

实现分析:

对于x实现逻辑右移n位,

可以首先对x执行x>>n得到算术右移n位后的结果x'

由于逻辑右移最高位补零,因此可以通过将算术右移的结果x'在移位过程中产生的高位全部清零得到逻辑右移的结果。这个操作可以通过掩码实现。

即对于 x=0x12345678,逻辑右移n=8位,

可以首先得到x的算术右移结果 y = x>>n = 0x??123456,

然后与mask = 0x00ffffff相与,得到结果 0x00123456

那么接下来要解决的就是mask如何得到的问题。

实际上可以通过 ((~(1<<31)>>n)<<1)+1得到mask

函数实现:

int logicalShift(int x, int n) {
	int mask;
	x = x>>n;
	mask = ~(1<<31);
	mask = ((mask>>n)<<1)+1;
	x = x&mask;
	return x;
}

bitCount函数

函数要求:

函数名bitCount
参数int
功能实现计算x中1的数目
要求! ~ & ^ | + << >>

实现分析:

  1. 首先这样得到了一个32位的掩码mask,其中每个16位块中的每个字节都是 1

  2. 接下来,通过多次按位与运算和右移操作,将整数 x 中每个16位块中的1的个数累加到变量 s 中。

  3. 随后,将 s 右移16位,并与原来的 s 相加,这是为了将高16位和低16位的计数相加到一起。

  4. 然后,创建新的掩码 mask0x0F0F,这个新的掩码用来将 s 中每个字节中(每8位)的1的个数相加到一起(使用与上一步骤不同的x0F0F形式的掩码是考虑到在进行相加求和的同时,和的上限16需要5位表示)。

  5. 最后,将 smask 进行按位与运算,然后将结果与 s 右移4位后与 mask 进行按位与运算的结果相加。这一步是为了将 s 中每个字节中的1的个数相加到一起。

  6. 最后一步,将ss 右移8位后的结果相加(计算得到全部位的和),并将最终结果与 0x3F 进行按位与运算(32位全1求和结果为32,因此最多需要6位数表示,可通过 0x3F 的掩码对高位清零,得到最终结果),最终返回计算出的1的个数。

函数实现:

int bitCount(int x) {
	int m1 = 0x11|(0x11<<8);
	int mask = m1|(m1<<16);
	int s = x&mask;
	s += (x>>1)&mask;
	s += (x>>2)&mask;
	s += (x>>3)&mask;
	s += s>>16;
	mask = 0xF|(0xF<<8);
	s = (s&mask) + ((s>>4)&mask);
	return (s+(s>>8))&0x3F;
}

conditional函数

函数要求:

函数名conditional
参数int , int, int
功能实现实现c语言中的三目运算 x ? y : z
要求! ~ & ^ | + << >>

实现分析:

根据三目运算的逻辑,x为真则返回yx为假则返回z

也即条件为 x的真假,那么 我们可以通过表达式 !!(x)判断x的真假,

因此我们可以通过表达式 condition = ~(!!x) + 1来表达条件,当x为真(即x不为0)时,condition=0xffffffff,当x为假(x=0)时condition=0

于是最终可以通过(condition&y)|(~condition&z)实现三目运算 x ? y : z

函数实现:

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

表二

tmin函数

函数要求:

函数名tmin
参数void
功能实现返回最小的补码
要求! ~ & ^ | + << >>

实现分析:

在32位系统int类型的变量中,最小的补码就是 0x80000000-2**(31)),可通过1<<31得到。

函数实现:

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

函数要求:

函数名fitsBits
参数int , int
功能实现x的补码是否可以表示成n位
要求! ~ & ^ | + << >>

实现分析:

x 左移32-n位后,再右移32-n位,仍与原来相同,则 x 补码可以表示成 n 位。

因为 x 在左移32-n位后,将高32-n位的信息全部都清除了,若再右移32-n位后仍然与原来相同,则说明x截断成后n位是不会改变x原本的数值的,即x的补码可以表示为n位。这是因为对于可以用n位补码表示的数x,上述过程的算数右移相当于对x进行扩展,不会改变它的数值信息。

而对于32-n位如何表示,根据补码的定义(即补码正负数之间的表示的转换关系)可以得出:-n = ~n+1;从而可以通过上述操作实现函数功能。

函数实现:

int fitsBits(int x, int n) {
  	int y;
	n = ~n + 1;
	y = (x<<(32+n))>>(32+n);
	return !(x^y);
}
dividePower2函数

函数要求:

函数名dividePower2
参数int , int
功能实现计算x除以2的n次幂
要求! ~ & ^ | + << >>

实现分析:

x除以2的n次幂,对于有符号整数,需要正负数分开讨论。

x为正数时,x除以2的n次幂,也就是 x>>n;

x为正数时,为使其向0舍入(即向上取整),需要对其进行修正,也即(x+2**n-1)>> n, 即(x+(1<<n)-1)>>n;

但是不能使用运算符 - ,但我们可以通过 ~0 = 0xffffffff = -1实现,也即(x+(1<<n+(~0)))>>n

而如何进行正负数的判断呢,我们可以通过 x>>31得到符号位s:正则s0x00000000,负则s0xffffffff

通过上述分析,可以得到最终实现代码如下。

函数实现:

int dividePower2(int x, int n) {
    return (x+((x>>31)&((1<<n)+(~0))))>>n;
}
negate函数

函数要求:

函数名negate
参数int
功能实现返回x的负数
要求! ~ & ^ | + << >>

实现分析:

根据补码中正负数表示的关系(即负数的表示等价于绝对值相同的正数的表示按位取反后加1),可以得知 -x = ~x+1

函数实现:

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

函数要求:

函数名howManyBits
参数int
功能实现计算表达x所需的最少位数
要求! ~ & ^ | + << >>

实现分析:

已知补码的最高表示位前面的所有数字应该都是全0(正数)或是全1(负数)。

将原数 x 与其 符号位 异或,正数仍为原数(即x),负数则为原数按位取反(即~x),在这里将结果表示为x'。不考虑符号位的情况下,正数中第一个1(从高位起)为表示所需的最少位数的最高位,负数中第一个0(从高位起)为表示所需的最少位数的最高位,当其取反后,该位成为第一个1。因此,我们可以通过寻找x'从高位起第一个为1的位次,便可得知,表达x所需的最少位数。

之后就是用二分法去测试最高位在哪里,比如bit_16=!!(temp>>16)<<4;结果不为0,说明最高非零位至少在16位以上,我们进一步将高16位留下观察。反之,如果为0,说明最高位介于0和16之间,于是不做处理,等的后面8位处理进行判断。

由此,通过二分法得到若干值,有的是16,8这类数,有的就是0,最后+1的偏置表示符号位,得到需要的位数。

函数实现:

int howManyBits(int x) {
  	int b16,b8,b4,b2,b1,b0;
	/*获得x的符号位*/
	int sign = x>>31;
	x = x^sign;
	/*二分法寻找第一个为1的位次*/
	b16 = !!(x>>16)<<4;
	x = x>>b16;
	b8 = !!(x>>8)<<3;
	x = x>>b8;
	b4 = !!(x>>4)<<2;
	x = x>>b4;
	b2 = !!(x>>2)<<1;
	x = x>>b2;
	b1 = !!(x>>1);
	x = x>>b1;
	b0 = x;
	return b16+b8+b4+b2+b1+b0+1;
}
isLessEqual函数

函数要求:

函数名isLessEqual
参数int , int
功能实现计算 x <= y ?
要求! ~ & ^ | + << >>

实现分析:

x <= y 由两种情况:

  • xy 同号,y-x 大于等于零
  • x, y 异号,且y为非负数

在以上思路的基础上进行分析实现,我们可以首先通过异或操作判断 xy 的符号异同(异或后符号位为1则同号,为0则异号)。然后在同号的情况下计算 sub=y-x(减法操作在上述实验中已经涉及) 并判断sub的符号(sub符号位为0则x<=y,反则反之);在异号的情况下,直接判断y的符号,若为0(为正)则x<=y

函数实现:

int isLessOrEqual(int x, int y) {
	// 计算 y-x
	int sub = y + ((~x)+1);
	// 用于判断x,y符号情况
	int s = (x^y)>>31;
	// 情况一
	int condition1 = (!s)&(!(sub>>31));
	// 情况二
	int condition2 = s&(!(y>>31));
	return condition1|condition2;
}
intLog2函数

函数要求:

函数名intLog2
参数int
功能实现计算x以2为底的对数,向下取整
要求! ~ & ^ | + << >>

实现分析:

由于真数必须大于零,因此这里不需要考虑负数情况,那么计算x以2为底的对数,向下取整,就相当于找到x的补码表示中从最高位起第一个为1的位次。这个操作实现与howManyBits函数中一致,同样采用二分法即可,此处不再赘述。

函数实现:

int intLog2(int x) {
  	int log = 0;
	log =!!(x>>16)<<4;
	log += !!((x>>log)>>8)<<3;
	log += !!((x>>log)>>4)<<2;
	log += !!((x>>log)>>2)<<1;
	log += !!((x>>log)>>1);
	return log;
}

表三

floatAbsVal函数

函数要求:

函数名floatAbsVal
参数unsigned int
功能实现计算f的绝对值的位级表示
要求Any integer/unsigned operations incl. ||, &&. also if, while

实现分析:

实验要求若ufNaN,则原数返回,否则返回器绝对值。
因此我们需要首先判断uf是否为NaN,也即检测uf是否阶码为全1,尾数不为0即可,可通过((uf&0x7f800000)>>23 == 255 && (uf<<9))语句实现。
uf不为NaN,在其绝对值的位级表示与原本一致,只需要使符号位s=0即可,可通过(uf&0x7fffffff)语句实现。

函数实现:

unsigned floatAbsVal(unsigned uf) {
  	// NaN返回NaN
	if((uf&0x7f800000)>>23 == 255 && (uf<<9))
		return uf;
	else
		return uf&0x7fffffff;
}
floatScale1d2函数

函数要求:

函数名floatScale1d2
参数unsigned int
功能实现计算0.5*f的位级表示
要求Any integer/unsigned operations incl. ||, &&. also if, while

实现分析:

实现计算0.5*f的位级表示需要分3种情况考虑:

  • ufNaNInfinity
  • 0.5*uf 为规格化值(Exp>1
  • 0.5*uf 为非规格化值(Exp<=1

ufNaNInfinity
NaNInfinity0.5后依旧为 NaNInfinity,直接返回

0.5* uf 为规格化值(Exp>1
0.5* uf 依旧为规格化值,则可直接通过Exp-1得到结果

0.5*uf 为非规格化值(Exp<=1
Exp<=1时,0.5*uf 为非规格化值(Exp=0uf为非规格化值,0.5*uf也为非规格化值;Exp=1uf为规格化值,但0.5 *uf为非规格化值),则通过将uf(摘除符号位)右移一位实现,但对于被移出的尾数(即最后一位),需要考虑其舍入,根据向偶舍入原则,仅当末尾两位为11时才需要向前进位,因此可通过语句if((uf&0x3)==0x3)判断是否需要进位,需要则uf = uf + 0x1;之后再在最前面加上符号位即可。
0也包含在这一情况中,可以被正确实现。

函数实现:

unsigned floatScale1d2(unsigned uf) {
  	// get s,E
        int s_ = uf&0x80000000;
        int Exp = ((uf&0x7f800000)>>23);

	// NaN,Infinity
	if(Exp == 255)
                return uf;
	// 当指数E大于1时,指数减一
	if(Exp>1) return (uf&0x807fffff)|((Exp-1)<<23);
	// 指数E<=1时,尾数右移一位
	else{
		if((uf&0x3)==0x3) uf = uf + 0x1;
		return ((uf&0x00ffffff)>>1)|s_;
	}
}
floatFloat2Int函数

函数要求:

函数名floatFloat2Int
参数unsigned int
功能实现计算(int)f的位级表示
要求Any integer/unsigned operations incl. ||, &&. also if, while

实现分析:

计算(int)f的位级表示,根据实验要求,可以分为以下几种情况:

  • 超出整型范围(包括 InfNaN),返回0x80000000
  • uf为绝对值小于1的小数(即(-1,1)区间),返回0
  • 符合整型范围的数

要区分上述三种情况和实现计算,我们需要获得uf的符号位并计算 指数EM。通过指数 E 可以直接区分出三种情况:当E>30时,将超出整型范围(因为int型还需要一个位次来表示符号位,因此E>=31时会超出范围);当E<0时,uf为绝对值小于1的小数;当0<=E<=30时,符合整型范围。

对于符合整型范围的数,由于经M = (uf&0x007fffff)+0x00800000操作得到的 M 相当于实际M乘上了2的23次幂,因此,当 E>23 时,M 应当再乘以2的(E-23)次幂(即M <<= (E_-23));否则 M >>=(23-E_)。最后再根据符号位s对于 M 做适当的处理即可(正数不变,负数取负)。

函数实现:

int floatFloat2Int(unsigned uf) {
  	
	// get s,E and M
	int s_ = uf>>31;
	int E_ = ((uf&0x7f800000)>>23)-127;
	int M_ = (uf&0x007fffff)+0x00800000;
	
	// overflow
	if(E_>30) return 0x80000000;
	// 0 or abs less than 1
	if(E_< 0) return 0;
	
	if(E_ > 23) M_ <<= (E_-23);
	else M_ >>= (23-E_);

	// positive and negative
	if(!s_) return M_;
	else return ~M_+1;
}

四、实验总结

在本段总结实验中遇到的问题或困难,以及对应的解决方法;对实验的建议等。
问题:

  • 在实现isLessEqual(int x, int y)函数时,我最初想采用通过!((y+(~x+1))>>31)语句来判断y>=x,但是实践中却无法通过测试,最终是在查找参考资料后,采用上述报告中的分情况讨论的方式实现的。
  • 在写这个语句的判断条件的时候 if((uf&0x3)==0x3) uf = uf + 0x1 我最初的写法是 if(uf&0x3 == 0x3),当时是考虑到 & 的优先级高于 ==,是可以实现我想要的逻辑运算的(即(uf&0x3)==0x3),但是最后测试却不通过,当我加上括号后就通过了,可能是实验的编译环境的底层运算分析不足的原因,但是也更加地提醒了我要规范的进行编程、规范地使用语言,使程序更健壮、可读性更高。
  • 27
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值