位运算概览与奇巧淫技

一、位运算概述

计算机对于计算数据的说话,都是以二进制来进行运算的,所以使用位运算相比直接使用(+、-、 *、/)运算符,要更高效,能显著调高代码在计算机中的执行效率。

二、位运算概览

符号描述运算规则
&两个位都为1时,结果才为1
|两个位都为0时,结果才为0
^异或两个位相同为0,相异为1
~非,取反0变1,1变0
<<左移不分正负,低位补0
>>右移用符号位填充高位
>>>无符号右移,逻辑右移用0填充高位
ab~aa&ba|ba^b
110110
011011
001000

异或

异或,可以理解为不进位加法:1+1=0,0+0=0,1+0=1

性质:

  1. 交换律 可任意交换运算因子的位置,结果不变
  2. 结合律 (即(ab)c==a(bc))
  3. 对于任何数x,都有xx=0,x0=x,同自己求异或为0,同0求异或为自己
  4. 自反性 ABB = A^0 = A,连续和同一个因子做异或运算,最终结果为自己

二进制数的原码、反码、补码

  • 正数的原码、反码、补码都是一致的

  • 负数的反码,除了符号位与原码一致,其余位都与原码相反

  • 负数的补码 = 反码 + 1

    数据存储和运算都是以补码的方式进行的

左移 <<

左移使,不管正负,低位补0

正数 a = 20 << 2

20的二进制补码:0001 0100

向左移动两位后: 0101 0000

结果:a = 80

负数 a = -20 << 2

-20的二进制原码:1001 0100

-20的二进制反码:1110 1011

-20的二进制补码:1110 1100

左移两位后的补码: 1011 0000

反码:1010 1111

原码:1101 0000

结果:a = -80

右移 >>

如果该数为正,则高位补0,若为负数,则高位补1

正数 a = 20 >> 2

20的二进制补码:0001 0100

向右移动两位后:0000 0101

结果:a = 5

负数 a = -20 >>2

-20的二进制补码:1110 1100

向右移动两位后:1111 1011

反码:1111 1010

原码:1000 0101

结果:a = -5

无符号右移 >>>

无符号右移>>> 也叫逻辑右移,若该数位正数,则高位补0,若该数为负数,则右移后高位同样补0

正数 a = 20 >>>2 与 a = 20 >> 2相同

结果:a = 5

负数 a = -20 >>> 2

(int类型)

-20的二进制原码:10000000 00000000 00000000 00010100

反码:11111111 11111111 11111111 11101011

补码:11111111 11111111 11111111 11101100

右移:00111111 11111111 11111111 11111011

结果:a = 1073741819

三、位运算的使用奇巧淫技

判断奇偶数

int num = 101;
System.out.println(num +"是"+((num&1)==0?"偶数":"奇数"));

5的二进制代码为101,5与1运算就是 101与001做&运算。

我们通过二进制判断奇偶数的话,看的是二进制最后一位,如果最后一位为0的话是偶数,为1的话是奇数

为什么?因为二进制除了最后以为,其他位都是2的幂次方,必然是偶数,所以我们通过判断最后一位是0或者1 就可以判断是奇数还是偶数。

获取二进制位是1还是0

int n = 86;
System.out.println( n+"的第五位是"+ (((n&(1<<4))>>4)==0?"0":"1")   );
System.out.println( n+"的第五位是"+ (((n>>4)&1)==0?"0":"1")   );

交换两个整数变量的值

int num1 = 10;
int num2 = 20;
num1 = num1^num2;
num2 = num1^num2;
num1 = num1^num2;
System.out.println("num1="+num1+", num2="+num2);

不用判断语句,求整数的绝对值

int a = -10;
int b = ((a >> 31) ^ a) + (a >>> 31);
System.out.println("a="+a+", b="+b);

整数取绝对值

搞了一整天,,取绝对值的还是懵懵的

先来分析一下 a = -86:

原码:1000 0000 0000 0000 0000 0000 0101 0110

反码:1111 1111 1111 1111 1111 1111 1010 1001

补码:1111 1111 1111 1111 1111 1111 1010 1010

取反:0000 0000 0000 0000 0000 0000 0101 0101

(+1): 0000 0000 0000 0000 0000 0000 0101 0110

由此可以得出,负数的绝对值为,补码取反+1,即 ~a+1。

int a = -86;
int b = ~a + 1;
System.out.println(b);

但是,这个还不能达到题目所要求的,不用判断,当假定 a = -86的时候,就已经知到 a 是负数了。

分析:任何一个数 异或0 结果为其本身,与 -1 (二进制全为1) 相当于取反

一个32 bit的int类型的正数 >> 31,结果为0,负数 >> 31 为-1。

即 (a>>31)的结果为 0 或者 -1。如果 a 为负数,(a>>31)^a 相当于 ~a,则需要 + 1 才能得到 a 的绝对值

所以代码为:((a >> 31) ^ a) + (a >>> 31)

  • 记得 a 是以补码的方式进行计算的,负数的补码不等于原码,计算前先转换!

或:

int a = -86;
int b = a >> 31;
    // 当a为正时:b = 0,a ^ b = a;
    // 当a为负时:b = -1,a ^ b = ~a;
    return (a ^ b) - b;// 也可以写为:(a ^ (a >> 31)) - (a >> 31)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值