按位与、或、异或运算以及其应用

我们都知道逻辑运算上有与(&&)、或(||)、非(!)三个运算。在计算机数值计算中,按位运算一共有6种:左移(<<)、右移(>>)、与(&)、或(|)、取反(~)、异或(^)运算。

1、定义:

1)按位与运算(&):双目运算符,其功能是参与运算的两数对应的二进位相与,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。参与运算的数以补码方式出现。

例如:

9&5=1
00001001 (9的补码)
&
00000101 (5的补码)
=
00000001 (1的二进制补码)。

 应用:

  • 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)
  • 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)

2)按位或运算(|):双目运算符,其功能是参与运算的两数对应的二进位相或,只要对应的二个二进位有一个为1时,结果位就为1。参与运算的两个数均以补码出现。

例如:

9|5=13,可写算式如下: 
00001001 (9的补码)
|
00000101 (5的补码)
=
00001101 (十进制为13)

应用:

  • 常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)

3)按位异或运算(^):双目运算符,其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。参与运算数仍以补码出现。

例如:

9^5=12,可写成算式如下: 
00001001 (9的补码)
^
00000101 (9的补码)
=
00001100 (十进制为12)

应用:

  • 与0相∧,保留原值;
  • 两个相同数异或,结果为0;
  • 异或操作满足结合律和交换律;
  • 将源操作数某些位翻转(1变0,0变1), (mask中特定位置1,其它位为0 s=s^mask)
  • 交换两个值,不用临时变量;(如下)
a = a^b;
b = a^b;
a = a^b; 

4)取反运算(~):单目运算符,其功能是对参与运算的数的各二进位按位求反。

例如:

~9的运算为: 
~(0000000000001001)结果为:1111111111110110

参考:https://blog.csdn.net/hguisu/article/details/7892596

2、使用二进制位运算设计权限:

1)原理:

我们都知道在linux中,x - 可执行权限,w - 可写权限 , r - 可读权限。其权限值分别是1,2,4。有没有想过为什么是1,2,4 而不是 1,2,3 呢?

观察发现,这些权限对应的int数值都是2的幂次方,对应的二进制如如下:

1:0000 0000 0000 0000 0000 0000 0000 0001
2:0000 0000 0000 0000 0000 0000 0000 0010
4:0000 0000 0000 0000 0000 0000 0000 0100
8:0000 0000 0000 0000 0000 0000 0000 1000

这些值在二进制表示中只有一位对应的是1,其余都是0,这样多个权限值进行叠加后不会相互覆盖,而且根据叠加后的权限值能否分解成对应的原来权限值。举一个反例:如果权限的原值用1,2,3表示,那么当给定一个权限3时,这个值就不知道是1和2叠加的权限,还是3原本的权限。

使用二进制的方式来表示权限的优点:

  • 运算速度块(位运算在计算机内部非常高效)
  • 通过按位与、或、异或操作高效的进行权限添加、删除和判断。

2)看一个例子:

private static void quanxianTest() {
		//1.定义权限:权限值使用int类型表示,且满足2的N次方
        int c = 1;//create,二进制表示:...0 0001
        int r = 2;//retrieve,二进制表示 ...0 0010
        int u = 4;//update,二进制表示 ...0 0100
        int d = 8;//delete,二进制表示 ...0 1000
        //其他权限:16、32、64...;
        
        //2.初始化权限
        int usera = c | r | u;//初始化用户A的权限,等价于c+r+u=7
        int userb = c | d;//初始化用户B的权限,等价于c+d=9
        int userc = 13;//初始化用户C的权限,即:c+u+d=13
        System.out.println("usera 权限值:"+usera);
        System.out.println("userb 权限值:"+userb);
        System.out.println("userc 权限值:"+userc);
        
        //3.判断权限
        if ((usera & u) == u) {//与操作结果只有两种:0或u
            System.out.println("usera有u权限");
        } 
        if ((userb & u) == 0) {
            System.out.println("userb没有u权限");
        } 
        if ((userc & c) != 0) {
            System.out.println("userc有c权限");
        } 
        
        //添加权限
        userb = userb | u;//或者userb = userb + u;
        if ((userb & u) == u) {
            System.out.println("userb有u权限,userb的权限值="+userb);
        } 
        
        //删除权限
        usera = usera & (~r);//或者usera = usera - r;
        if ((usera & r) == 0) {
            System.out.println("user a没有r权限,usera的权限值="+usera);
        } 
	}

3)总结:

使用二进制的与或非来设计权限的步骤:

  1. 定义权限,权限的int值分别是2的幂次方,即1、2、4、8...
  2. 添加权限时使用或(|)运算,最终权限值在数值上=对应的权限值加在一起;
  3. 删除权限时使用与(&)和异或(~)运算,最终权限值在数值上=原权限值-删除的权限值
  4. 判断权限是使用与(&)运算,结果等于非零(或对应权限的值)时表示有该权限,结果等于0表示没有该权限;

注:只有当权值是2的幂次方时,或(|)运算相当于加法、与(&)运算相当于减法

https://www.cnblogs.com/jifeng/archive/2013/03/03/2941159.html

3、使用二进制位运算存储数据信息:(类似bitmap)

 在设计程序时,我们经常会运到一种情况:某个类有多个属性值时,且这些属性的取值为1和0。这时,我们的常规思路是每个属性使用一种类型表示(boolean或int),优点是清晰,缺点是浪费空间(假设我们有2kw个这样的对象)。

为了节省空间,我们使用一个int类型的变量,来表示类的所有属性。int类型变量值转成二进制位,每一位表示一个属性值(0或1)——bitMap算法思想

1)原理:

根据位运算的定义,我们可以得出以下结论:

  • 将int型变量a的第k位清0:即a=a&~(1<<k)
  • 将int型变量a的第k位置1:即a=a|(1<<k)
  • 取int型变量a的第k位:即a>>k&1

例如:

将31的第8位设置成1,可以表示成:31的二进制|(1<<8)
1)31的二进制:
00000000 00000000 00000000 00011111
2)1的二进制:
00000000 00000000 00000000 00000001
左移动8位:
00000000 00000000 00000001 00000000 
3)31|(1<<8)表示成:
00000000 00000000 00000000 00011111
|
00000000 00000000 00000001 00000000 
=
00000000 00000000 00000001 00011111 

注:int类型表示成的二进制数据中,最右边是第0位,左移k位,就是将第0位移动到第k位,右边补0

2)示例:

private static void test0() {
		int a = 31;
		System.out.println(toBinaryString(a));//按2进制输出
		//00000000 00000000 00000000 00011111 
		
		//将int型变量a的第k位清0,即a=a&~(1<<k)
		int b = a & ~(1<<3);
		System.out.println(toBinaryString(b));
		//00000000 00000000 00000000 00010111 
		
		//将int型变量a的第k位置1, 即a=a|(1<<k)
		int c = a | (1<<8);
		System.out.println(toBinaryString(c));
		//00000000 00000000 00000001 00011111 
		
		//取int型变量a的第k位,(k=0,1,2……sizeof(int)),即a>>k&1
		int d = a >> 2 & 1;
		System.out.println(d);//1
		int d2 = a & (1<<2);
		System.out.println(d2);//4
	}
	
	public static String toBinaryString(int a) {
		StringBuilder sb = new StringBuilder();
		for(int i=0;i<32;i++){
			int t = (a & 0x80000000>>>i)>>>(31-i);
			//System.out.println(t);
			sb.append(t);
			if ((i+1) % 8 ==0) {
				sb.append(" ");
			}
		}
		return sb.toString();
   }

发布了800 篇原创文章 · 获赞 460 · 访问量 436万+

猜你喜欢

转载自blog.csdn.net/liuxiao723846/article/details/105039493
今日推荐