【百度知道系列】关于位操作的循环位移

因为疫情在家,在比较闲的时候就会到百度知道答题,自从有一个老账号(曾经没绑定手机的)丢失了以后,新号的财富值就很伤,有时候下文档不方便,最近冲了一冲。

同时,发现很多程序类的问题比较基础吧,自己也算是经历过了那个阶段,所以既然写了,就发上来做留存吧,供大家参考。

————————————————————————————————————————

本次问题是编程中按位操作的问题。位运算的效率是非常高的,不过由于现在计算机处理速度比较快,感觉日常编程时候不是很在意这方面的内容。我的环境用的C++

位操作的一般操作符包括:

位运算操作符
操作符功能示例结果
<<左移操作符,是个二元的操作符。前面的操作数按照后面操作数规定的数字移动相关位数,末尾补01<<500000001->00010000
>>右移操作符,基本同上,不过负数的话,会补1,正数补0。(负数补码存储)99>>101100011->00110001
-5>>111111011->11111101
~按位取返,这是个一元操作符~100000001->11111110
|按位或,二元操作符,两个数同一位上,有一个是1,结果就是11|3

00000001|00000011

->00000011

&按位与,二元操作符,两个数同一位上都是1,结果才是11&3

00000001&00000011

->00000001

^

按位异或,二元操作符

也就是两个数同一位上不相同,结果就是1,相同,结果就是0

1^3

00000001^00000011

->00000010

所以,位循环操作时候,只需要对根据上述性质进行处理即可。这篇文章主要就是对循环位位移操作的。

也就是:如果一个二进制数a=\left ( 10110101 \right )_{2},则循环右移3位应该得到\left ( 10110110 \right )_{2},即本应位移丢失的数据,放到末尾或者开头。

这里直接上代码,并用实例在注释中进行了说明,请参考。核心在于,将会丢失的位提前取出来,并对应放到对的位置,通过或运算(提前保证空出来的位置都是0)将对应的位补回去。

int lrmove(int value, int n)//输入0x8000000C,和5的情况下
{
	int high = 0, low = 0, length = sizeof(int) * 8, result = 0;
	//因为编译器不同,为了普适我这里用了sizeof获取对当前系统的int长度

	/*因为右移过程中会出现补0与补1的情况,那我们在第一次移动一格之后,保证高位变成0,以后就都可以补 
      0了*/
	int	mask = ~(1 << length - 1);//把1左移到最大位再取反,0x7FFFFFFF
	n = n % length;//没必要多移动几圈,取最短就可以了,而且我下面的方法也不适用多移动几圈的
	if (n == 0)
		return value;
	//左移肯定是补0,所以没关系,随便移动
	high = value << n; //0000 0000 0000 0000 0000 0001 1000 0000=0x00000180
	low = (value >> 1) & mask;//右移一位,再高位置0其他的不变,0x40000006
	low = low >>(length - n - 1);//已经是正数,再右移后面的部分0x00000010
	result= high | low;//0000 0000 0000 0000 0000 0001 1001 0000=0x00000190
	return result;
}
int rrmove(int value, int n)//输入0x8000000C,和5的情况下
{
	int high = 0, low = 0, length = sizeof(int) * 8, result = 0;
	//因为编译器不同,为了普适我这里用了sizeof获取对当前系统的int长度

	/*右移比较麻烦,正数补0,负数补1,所以为了让高位每次都肯定补0,
	我们先右移动1位,然后把最高位变成了0,就需要我下面定义的这个mask*/
	int	mask = ~(1 << length - 1);//把1左移到最大位再取反,0x7FFFFFFF
	n = n % length;//没必要多移动几圈,取最短就可以了,而且我下面的方法也不适用多移动几圈的
	if (n == 0)
		return value;
	high = value << length - n;//0x60000000
	low = (value >> 1)&mask;//0x40000006
	low = low >> n - 1;//0x04000000
	result = high | low;//0x64000000
	return result;
}

那以右移动为例,按照移动的次数划分,分割点前是高位h,分割点后是低位l:

右移的目标可以看成将高低位进行互换。那么:我们通过右移动原始,可以将高位变成低位,同理,左移动,可以把低位变成高位,二者位置对应对了之后,就可以直接或操作,将两个数拼接起来了。

比如(以下都是二进制表示):

a=11001010循环右移5位

则向右移动第一次l=a>>1=11100101,这个原始数字首位是1,意味着是个负数,所以默认补1的。

我们定义一个mask=01111111,这样,当二者取&运算的时候,最高位一定会被置0,其他位则会保留原始状态

数值会变成l=l&mask=01100101

再次移动,就会默认补0了,那么已经移动了1次,再移动剩下4次,就可以了,也就是结果的低位会变成l=l>>4=00000110

对于低位,就比较简单了,如果数值长n=8,要求右移动5位,也就是低的5位会变到高位上,那我左移动n-5也就是3位,低位上的数字自然就变到高位了。

所以,我们所作的就是结果的高位h=a<<(n-5),这里的n涉及系统或者编译环境对一个数字长度的定义,可能是16位,可能是32位,因此我在程序中利用sizeof获取一下字节长度再乘以8,就得到位长度了。这里文字描述按照8位的数进行的解释,32位整数的变化参考代码注释

操作后,h=a<<3=01010000

我们已经知道,l是低位对,高位0的部分;h是高位对,低位补0的;因此,二者对应把1赋值到一起,就是结果了。

最后,result=h|l=01010110

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值