java的位运算

 在平常的Java开发中,位运算使用的很少,使用的更多的是算数运算(+、-、*、/、%)、关系运算(<、>、<=、>=、==、!=)和逻辑运算(&&、||、!),因此相对来讲对位运算不是那么熟悉,本文将以Java的位运算来详细介绍下位运算及其应用。算法

一、 位运算起源架构

  位运算起源于C语言的低级操做,Java的设计初衷是嵌入到电视机顶盒内,因此这种低级操做方式被保留下来。所谓的低级操做,是由于位运算的操做对象是二进制位,可是这种低级操做对计算机而言是很是简单直接,友好高效的。在简单的低成本处理器上,一般位运算比除法快得多,比乘法快几倍,有时比加法快得多。虽然因为较长的指令流水线和其余架构设计选择,现代处理器一般执行加法和乘法的速度与位运算同样快,但因为资源使用减小,位运算一般会使用较少的功率,因此在一些Java底层算法中,巧妙的使用位运算能够大量减小运行开销。spa

二、 位运算详解架构设计

  Java位运算细化划分能够分为按位运算和移位运算,见下表。设计

细化对象

符号ci

描述资源

运算规则开发

按位运算get

&

两位都为1,那么结果为1

|

有一位为1,那么结果为1

~

~0 = 1,~1 = 0

^

异或

两位不相同,结果为1

移位运算

<< 

左移

各二进制位所有左移N位,高位丢弃,低位补0

>> 

右移

各二进制位所有右移N位,若值为正,则在高位插入 0,若值为负,则在高位插入 1

>>> 

无符号右移

各二进制位所有右移N位,不管正负,都在高位插入0

  在进行位运算详解以前,先来普及下计算机中数字的表示方法。对于计算机而言,万物皆0、1,全部的数字最终都会转换成0、1的表示,有3种体现形式,分别是:原码、反码和补码

  原码:原码表示法在数字前面增长了一位符号位,即最高位为符号位,正数位该位为0,负数位该位为1.好比十进制的5若是用8个二进制位来表示就是00000101,-5就是10000101。

  反码:正数的反码是其自己,负数的反码在其原码的基础上,符号位不变,其他各个位取反。5的反码就是00000101,而-5的则为11111010。

  补码:正数的补码是其自己,负数的补码在其原码的基础上,符号位不变,其他各位取反,最后+1。即在反码的基础上+1。5的反码就是00000101,而-5的则为11111011。

  了解了这几个概念后,咱们如今先记住一个结论,那就是在计算机系统中,数字一概用补码来表示、运算和存储,具体的缘由能够看这篇文章的讨论,这里不作更多讨论,由于不是本文的重点。

2.1 与运算(&)

  规则:转为二进制后,两位为1,则结果为1,不然结果为0。

  举例:

十进制

二进制(正数原码、反码、补码一致)

10

00000000000000000000000000001010

&12

&00000000000000000000000000001100

=

=

8

00000000000000000000000000001000

 

十进制

二进制(原码)

-6

10000000000000000000000000000110

&-2

&10000000000000000000000000000010

十进制

二进制(反码)

-6

11111111111111111111111111111001

&-2

&11111111111111111111111111111101

十进制

二进制(补码)

-6

11111111111111111111111111111010

&-2

&11111111111111111111111111111110

=

=

-6

11111111111111111111111111111010

  最后的计算结果11111111111111111111111111111010仍是补码的形式,要看其十进制,还须要先转成二进制原码。

  先转反码:11111111111111111111111111111010-1=11111111111111111111111111111001,得反码11111111111111111111111111111001。

  再转原码:在反码的基础上转原码,符号位不变,其余各位取反,得10000000000000000000000000000110。第一位1表明负数,后面0110转成十进制是6,得-6。

2.2 或运算(|)

  规则:转为二进制后,有一位为1,则结果为1,不然结果为0。

  举例:

十进制

二进制(正数原码、反码、补码一致)

10

00000000000000000000000000001010

|12

|00000000000000000000000000001100

=

=

14

00000000000000000000000000001110

 

十进制

二进制(原码)

-6

10000000000000000000000000000110

|-2

|10000000000000000000000000000010

十进制

二进制(反码)

-6

11111111111111111111111111111001

|-2

|11111111111111111111111111111101

十进制

二进制(补码)

-6

11111111111111111111111111111010

|-2

|11111111111111111111111111111110

=

=

-2

11111111111111111111111111111110

 

2.3 非运算(~)

  规则:转为二进制后,~0 = 1,~1 = 0。

  举例:

十进制

二进制(正数原码、反码、补码一致)

~7

~00000000000000000000000000000111

=

=

-8

11111111111111111111111111111000(补码需转换为原码)

  11111111111111111111111111111000-1得反码,能够把1000当作是0112,得反码11111111111111111111111111110111。根据反码得原码10000000000000000000000000001000。

十进制

二进制(原码)

~(-6)

~10000000000000000000000000000110

十进制

二进制(反码)

~(-6)

~11111111111111111111111111111001

十进制

二进制(补码)

~(-6)

~11111111111111111111111111111010

=

=

5

00000000000000000000000000000101(正数原码、反码、补码一致)

 

2.4 异或运算(^)

  规则:转为二进制后,两位不相同,结果为1,不然为0。

  举例:

十进制

二进制(正数原码、反码、补码一致)

15^2

00000000000000000000000000001111

^00000000000000000000000000000010

=

=

13

00000000000000000000000000001101

 

2.5 左移运算(<<)

  规则:转为二进制后,各二进制位所有左移N位,高位丢弃,低位补0。

  举例:

十进制

二进制(正数原码、反码、补码一致)

2<<2

00000000000000000000000000000010

=

0000000000000000000000000000001000

8

00000000000000000000000000001000

 

十进制

二进制(先取补码 再对补码操做位移)

-2<<2

10000000000000000000000000000010(原码)

 

11111111111111111111111111111101(反码)

 

11111111111111111111111111111110(补码)

 

1111111111111111111111111111111000

 

11111111111111111111111111111000(补码)

 

11111111111111111111111111110111(反码)

-8

10000000000000000000000000001000(原码)

 

2.6 右移运算(>>)

  规则:转为二进制后,各二进制位所有右移N位,若值为正,则在高位插入 0,若值为负,则在高位插入 1。

  举例:

十进制

二进制(正数原码、反码、补码一致)

2>>2

00000000000000000000000000000010

=

0000000000000000000000000000000010

0

00000000000000000000000000000000

 

十进制

二进制(先取补码 再对补码操做位移)

-6>>2

10000000000000000000000000000110(原码)

 

11111111111111111111111111111001(反码)

 

11111111111111111111111111111010(补码)

 

1111111111111111111111111111111010

 

11111111111111111111111111111110(补码)

 

11111111111111111111111111111101(反码)

-2

10000000000000000000000000000010(原码)

 

2.7 无符号右移运算(>>>)

  规则:转为二进制后,各二进制位所有右移N位,不管正负,都在高位插入0。

  举例

十进制

二进制(先取补码 再对补码操做位移)

-1>>>1

10000000000000000000000000000001(原码)

 

11111111111111111111111111111110(反码)

 

11111111111111111111111111111111(补码)

 

011111111111111111111111111111111

 

01111111111111111111111111111111(补码)

 

01111111111111111111111111111110(反码)

溢出,只能表示到int的最大值2147483647

10000000000000000000000000000001(原码)

 

三、 应用

3.1 不用额外的变量实现两个数字互换

  见参考资料中的BitOperationTest,方法reverse经过三次异或操做完成了两个变量值的替换。

  证实很简单,咱们只须要明白异或运算知足下面规律(实际不止以下规律):

  0^a = a,a^a = 0;

  a ^ b = b ^ a;

  a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c;

  a ^ b ^ a = b;

  假设a,b两个变量,通过以下步骤完成值交换:a=a^b,b=b^a,a=a^b。

  证实以下:

  由于a ^ b = b ^ a,又a=a^b,b=b^a。故b=b^a= b^ (a^b)=a。

  继续a=a^b,a=(a^b) ^ b^ (a^b),故a=b。完成值交换。

3.2 不用判断语句实现求绝对值

  公式以下:(a^(a>>31))-(a>>31)

  先整理一下使用位运算取绝对值的思路:若a为正数,则不变,须要用异或0保持的特色;若a为负数,则其补码为原码翻转每一位后+1,先求其原码,补码-1后再翻转每一位,此时须要使用异或1具备翻转的特色。

  任何正数右移31后只剩符号位0,最终结果为0,任何负数右移31后也只剩符号位1,溢出的31位截断,空出的31位补符号位1,最终结果为-1.右移31操做能够取得任何整数的符号位。

  那么综合上面的步骤,可获得公式。a>>31取得a的符号,若a为正数,a>>31等于0,a^0=a,不变;若a为负数,a>>31等于-1 ,a^-1翻转每一位。

3.3 判断一个数的奇偶性

  经过与运算判断奇偶数,伪代码以下:

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

  奇数最低位确定是1,而1的二进制最低位也是1,其余位都是0,因此全部奇数和1与运算结果确定是1。

相关文章
相关标签/搜索