位运算

文章采摘自百度百科http://baike.baidu.com/view/379209.htm,有删减,将其pascal代码改为c语言代码。

简介:

程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。

如i*94 = i*(64 + 32 - 2) = i*64 + i*32 –i*2 = i<<6 + i<<5 – i<<1;后者运算速率远大于前者。

运算符号

假设AB都是整型类型

Pascal

C

Java

A and B

A&B

A&B(同1为1)

A or B

A|B

A|B(同0为0)

A xor B

A^B

A^B(异或,相同为0)

not A

~A

~A(按位取反)

A shl B

A<<B

A<<B(左移,left)

A shr B

A>>B

A>>B(无符号右移,right)

-

-

A>>>B(带符号右移)

 

 

=== 1. and运算 ===

and运算通常用于二进制取位操作,例如一个数and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数。

相同位的两个数字都为1,则为1;若有一个不为1,则为0。(&;或者and)

00101

11100

----------------

00100

=== 2. or运算 ===

or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。(|或者or)

相同位只要一个为1即为1。

00101

11100

----------------

11101

=== 3. xor运算 ===

异或的符号是⊕。按位异或运算, 对等长二进制模式按位或二进制数的每一位执行逻辑按位异或操作. 操作的结果是如果某位不同则该位为1, 否则该位为0.

xor运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a xor b) xor b = a。xor运算可以用于简单的加密,比如我想对我MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 20665500,我就把20665500告诉MM。MM再次计算20665500xor 19880516的值,得到1314520,于是她就明白了我的企图。

相同位不同则为1,相同则为0。(^或者xor)

00101

11100

----------------

11001

延伸:

于是我们就有了一个看起来非常诡异的swap过程:

int swap(int a, int b){

         a=a^ b;

         b=a ^ b;

         a=a ^ b;

}

=== 4. not运算 ===

not运算的定义是把内存中的0和1全部取反。使用not运算时要格外小心,你需要注意整数类型有没有符号。如果not的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用00到$FFFF依次表示的。下面的两个程序(仅语言不同)均返回65435。

=== 5. shl运算 ===

a shl b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2= 400。可以看出,a shl b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。

通常认为a shl 1比a * 2更快,因为前者是更底层一些的操作。因此程序中乘以2的操作请尽量用左移一位来代替。

定义一些常量可能会用到shl运算。你可以方便地用1 shl 16 - 1来表示65535。很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量。

=== 6. shr运算 ===

和shl相似,a shr b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用shr 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用shr代替除法运算可以使程序效率大大提高。最大公约数的二进制算法用除以2操作来代替慢得出奇的mod运算,效率可以提高60%。

这里注意一下负数的左移和右移:

1)负数的右移:负数右移的话,由于要保持它是负数,所以负数的二进制的右边补1。如果一直右移的话,最后就就变成0xFFFFFFFF 即-1

如: -4>>1 为-2 ;-4>>2为-1

2)负数的左移:跟正整数左移一样,右边补0,一直左移的话,最后就是0啦。-2<<2为-4 ; -2<<31为0

优先级

C语言中位运算符之间,按优先级顺序排列为

1

~

2

<<>>

3

&

4

^

5

|

6

&= ^= |= <<= >>=

 

 

常见的二进制位的操作。

功能

示例

位运算

去掉最后一位

101101->10110

x shr 1

在最后加一个0

101101->1011010

x shl 1

在最后加一个1

101101->1011011

x shl 1+1

把最后一位变成1

101100->101101

x or 1

把最后一位变成0

101101->101100

x or 1-1

最后一位取反

101101->101100

x xor 1

把右数第k位变成1

101001->101101,k=3

x or (1 shl (k-1))

把右数第k位变成0

101101->101001,k=3

x and not (1 shl (k-1))

右数第k位取反

101001->101101,k=3

x xor (1 shl (k-1))

取末三位

1101101->101

x and 7

取末k位

1101101->1101,k=5

x and (1 shl k-1)

取右数第k位

1101101->1,k=4

x shr (k-1) and 1

把末k位变成1

101001->101111,k=4

x or (1 shl k-1)

末k位取反

101001->100110,k=4

x xor (1 shl k-1)

把右边连续的1变成0

100101111->100100000

x and (x+1)

把右起第一个0变成1

100101111->100111111

x or (x+1)

把右边连续的0变成1

11011000->11011111

x or (x-1)

取右边连续的1

100101111->1111

(x xor (x+1)) shr 1

去掉右起第一个1的左边

100101000->1000

x and (x xor (x-1))(或 x and (-x))

 

题型举例:

奇偶性

我们可以用下面的代码来计算一个32位整数的二进制中1的个数的奇偶性,当输入数据的二进制表示里有偶数个数字1时程序输出0,有奇数个则输出1。例如,1314520的二进制101000000111011011000中有9个1,则x=1314520时程序输出1。

 代码:

//针对三十二位数(int)
//每异或一次,最后一位代表加上自身往前供2^i个位置的1的个数
//最后取出最后一位的值
int is_Odd_Bits(int a){
    a = a^(a>>1);
    a = a^(a>>2);
    a = a^(a>>4);
    a = a^(a>>8);
    a = a^(a>>16);
    return a&1;
}

绝对值

只用位运算来取绝对值。

int int_abs(int x)
{
	//int 是32位,以补形式存储,如果是负数,则y为-1,如果是正数,则y为0
	//11111111 11111111 111111111 11111111这是-1的补码
	//return (x^x>>31) - x>>31;//由补码求原码取反加1,-y等于+1
	return (x^(x>>31)) - (x>>31);//另外,运算符的优先级为:~优先于+-*/优先于>><<优先于&^
}

高低位交换

给出一个小于2^32的正整数。这个数可以用一个32位的二进制数表示(不足32位用0补足)。我们称这个二进制数的前16位为“高位”,后16位为“低位”。将它的高低位交换,我们可以得到一个新的数。试问这个新的数是多少(用十进制表示)。

例如,数1314520用二进制表示为0000 0000 0001 0100 0000 1110 1101 1000(添加了11个前导0补足为32位),其中前16位为高位,即00000000 0001 0100;后16位为低位,即0000 1110 1101 1000。将它的高低位进行交换,我们得到了一个新的二进制数0000 1110 1101 1000 0000 0000 0001 0100。它即是十进制的249036820。

代码:

int High_Low_Bit_Swap(int x){
    return (x>>16)|(x<<16);
}

逆序

下面的程序读入一个32位整数并输出它的二进制倒序后所表示的数。

输入:1314520 (二进制为00000000000101000000111011011000)

输出:460335104 (二进制为00011011011100000010100000000000)

代码:

int Reverse_Bit(int x){
    x= (x & 0x55555555)<<1 | (x & 0xAAAAAAAA)>>1;
    x= (x & 0x33333333)<<2 | (x & 0xCCCCCCCC)>>2;
    x= (x & 0x0F0F0F0F)<<4 | (x & 0xF0F0F0F0)>>4;
    x= (x & 0x00FF00FF)<<8 | (x & 0xFF00FF00)>>8;
    x= (x & 0x0000FFFF)<<16 | (x & 0xFFFF0000)>>16;
    return x;
}

补充:

位数不同的运算数之间的运算规则补充:

C语言中,位运算的对象可以是整型(int)和字符型(char)数据。(整形数据可以直接转化成二进制数,字符型数据在内存中以它的ASCII码值存放,也可以站化成二进制数)当两个运算数类型不同时,位数亦会不同。遇到这种情况,系统将自动进行如下处理:

1将两个运算数右端对齐。

2 再将位数短的一个运算数往高位扩充,即:无符号数和正整数左侧用0补全;负数左侧用1补全;然后对位数相等的两个运算数,按位进行运算。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值