汇编-05-位运算

汇编-05-位运算

前言

  • 日常开发中位运算不是很常用,但是巧妙的使用位运算可以大量的减少运行的开销,优化算法。
  • 举个例子,翻转的操作比较常见,比如初始值是1,操作一次变成0,再操作一次变成1。 可能的做法是使用三木运算符,判断原始值是1还是0,如果是1,设置位0。否则设置位0,但是使用位运算,不用判断原始值,直接改变值就可以。1^num //num 为原始值。
  • 当然,一句代码可能对性能没有什么影响,到那时在高并发,大数据量的情况下可以节省很多开销。
  • 以下是整理的关于java位运算的部分内容,如有错误,还请指出,以共同进步,先行致谢。

1. 位运算符

1.1 java支持的位运算符

  • & : 按位与
  • | :按位或
  • ~ : 按位非
  • ^ : 按位异或
  • << : 左位移运算符
  • >> : 右位移运算符
  • >>> 无符号右移运算符
  • 注意:任何语言都没有无符号左移运算符
  • 位运算符中:除 ~ 以外,其余均为二元运算符。操作数只能是整型和字符型数据。

Java使用补码来表示二进制数,在补码表示中,最高位是符号位,正数的符号位为0,负数为1。

  • 补码的规定如下:对于正数来说,最高位是0,其余各位代表数值本身的二进制表示,如 + 42 的 补码 是 0010 1010
  • 补码对于负数来说,最高位是 1 ,把其余的位按位取反变成反码,然后对整个数+1,就是该数字的补码 , -1 的补码是 1111 1111 (在java中 -1 的 补码 是 1111 1111 1111 1111 ,因为java中的int是32位的)

1.2 按位与(&)

按位与的运算规则

操作数1 0 0 1 1
操作数2 0 1 0 1
& 0 0 0 1

总结

  • 两个数同时位1的时候,结果为1
  • 其余全为0(只要有一个操作数为0,结果就为0)

举例:

mark

mark

mark

1.3 按位或(|)

按位或的运算规则

操作数1 0 0 1 1
操作数2 0 1 0 1
按位或 0 1 1 1

总结

  • 只有两个操作数对应位同时为0,结果才是0
  • 其余全是1(只要有一个1,那么结果就是1)

1.4 按位非(~)

操作数 0 1
按位或 1 0

在求负数的源码中使用过。

1.5 按位异或(^)

  • 按位异或的运算规则
操作数1 0 0 1 1
操作数2 0 1 0 1
按位异或 0 1 1 0

总结:

  • 不一样就是1,一样就是0

1.6 左位移(<<)

  • 算数左移(<<) : 符号位不变,低位补0.
  • 2 << 2 的结果是8.

举例:2 << 2 的结果是8.

mark

总结:

  • 当移动的位数超过数字本身位数的时候,那么不就全部需要补0的操作吗?
  • 实际上不是的,java不可能做这么浪费资源的事情。在真正执行位移前,其对要移动的位数做了一些预处理,如32处理为0,-1处理为31

1.7 右位移(>>)

  • 低位溢出,符号位不变,并用符号位补溢出的高位。
  • -6 >> 2 结果是 -2 (相当于除3)

mark

1.8 无符号右移(>>>)

  • 低位溢出,高位补0
  • 注意,无符号右移(>>>) 中的符号位(最高位)也跟着在变。
  • 无符号的意思是将符号位当作数字位来看待。
  • -1 >>> 1 的结果是2141483647

这个数字应该比较熟悉,看两个输出语句就知道是什么了:

1
2
3
4
5
6
7
System.out.println(Integer.toBinaryString(-1>>>1));
System.out.println(Integer.toBinaryString(Integer.MAX_VALUE));

输出结果为:

1111111111111111111111111111111
1111111111111111111111111111111

mark

除了使用-1>>>1能得到Integer.MAX_VALUE,以下的也能得到同样的结果:

1
2
3
4
//maxInt
System.out.println(~(1 << 31));
System.out.println((1 << -1) -1);
System.out.println(~(1 << -1));

使用位运算往往能很巧妙的实现某些算法完成一些复杂的功能。

2. 常见使用

2.1 m*2 ^n

  • 可以使用 m << n 求得结果:

    • System.*out*.println("2^3=" + (1<<3));//2^3=8
    • System.*out*.println("3*2^3=" + (3<<3));//3*2^3=24
  • 计算结果是不是很正确呢? 如果非要说 2 << -1 为什么不等于0.5? 前面说过,位运算的操作数只能是整型和字符型。再求int所能表示的最小值的时候,可以使用:

    1
    2
    3
    # minInt
    System.out.println(1 << 31);
    System.out.println(1 << -1);
  • 可以发现左移31位和-1位所得的结果是一样的。同理,左移30位和左移-2所得的结果也是一样的。移动一个负数为,是不是等于右移该负数位的绝对值呢?输出一下发现结果不是的。
  • java中 Int 所能表示的最大值是31位,加上符号位共32位。在这里有如下的位移法则:
    • 法则1: 任何数左移(右移)32的倍数位等于该数本身
    • 法则2:在位移运算 m << n 的计算中,若n 是正数,则实际移动的位数是 n % 32;若 n是负数,则实际移动的位数是 (32 + n % 32) ,右移同理
    • 左移是乘以2的幂,右移是除以2的幂。

2.2 判断一个数的奇偶性

n & 1 == 1? 奇数:偶数

  • 为什么与1能判断奇偶?
  • 所谓的二进制就是满2进1,那么好了,偶数的最低位肯定是0(恰好满2,对不对?)
  • 同理,奇数的最低为肯定是1,对于int类型的1,前31位都是0,无论是1&0 还是 0 & 0结果都是0
  • 那么有区别的肯定就是最低为上的 1 了,若 n 的二进制最低位为是1(奇数),反则就是结果就是0(偶数)

2.3 不使用临时变量交换两个数字

在int[]数组首尾互换中,是不看到过这样的代码:

参考博客链接https://www.cnblogs.com/findbetterme/p/10787118.html

打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2019-2022 Zhuuu
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信