讲讲我对Java位运算的理解
来源:互联网 发布:一般淘宝客佣金关闭 编辑:程序博客网 时间:2024/04/19 12:17
阅读前提:
1、懂二进制、十进制、十六进制,并知道如何转换。
2、懂Java。
内容概括:
Java位运算相关,包括三个移位、四个位运算,很简单很基础很低端。
正文:
Java中的位运算符有以下几种:
<<左移
>>右移
>>>无符号右移(又称逻辑右移,相应的>>就称为算术右移)
与(&)、非(~)、或(|)、异或(^)
以上所有的运算,都是基于二进制数发生的。比如你要手动计算8左移之后变成多少,必须先把8转换成二进制的1000才可以。
Java里写一个数字的时候,直接写就是十进制。如果写成0x开头的,就是十六进制数。比如0x10其实就是16。(八进制是0开头,不表)。似乎不能输入二进制数字,大概是因为太长了。但是可以用Integer.toBinaryString(int)方法来得到一个int值的二进制表示。
还应该知道,Java里的基本数据类型int和long是有上下限的。如果转换成二进制的话,int是32位,long是64位。学过计组的应该知道,为了区分正负数,用二进制数的第一位来当符号,0正1负。也就是说真正的数字是int后面的31位和long后面的63位(不过负数并不是简单地把正数的首位改成1而已,负数是用补码来存储的,不了解补码可以去百度一下)。查JDK文档就会发现,它们的上下限分别是-231~231-1和 -263~263-1。顺带一提short是16位,byte是8位。但是这两个类型的变量一位运算,就会自动变成Int……因为有上下限,所以如果运算得太过分的话,会发生上溢或者下溢,然后数字就会变得很奇怪。
然后看以下程序:
int n = 3; System.out.println(n); System.out.println(n<<1); System.out.println(n>>1); System.out.println(n>>>1); System.out.println("*****"); System.out.println(Integer.toBinaryString(n)); System.out.println(Integer.toBinaryString(n<<1)); System.out.println(Integer.toBinaryString(n>>1)); System.out.println(Integer.toBinaryString(n>>>1)); System.out.println("*****"); n=-3; System.out.println(n); System.out.println(n<<1); System.out.println(n>>1); System.out.println(n>>>1); System.out.println("*****"); System.out.println(Integer.toBinaryString(n)); System.out.println(Integer.toBinaryString(n<<1)); System.out.println(Integer.toBinaryString(n>>1)); System.out.println(Integer.toBinaryString(n>>>1));
运行结果:
36
1
1
*****
11
110
1
1
*****
-3
-6
-2
2147483646
*****
11111111111111111111111111111101
11111111111111111111111111111010
11111111111111111111111111111110
1111111111111111111111111111110
通过观察结果就可以了解以下内容:
左移n位,其实就是乘以2n;右移n位,则是除以2n。
左移时,右边补的是0。算术右移时,左边补的是符号位(也就是正数补0,负数补1)。逻辑右移时,左边补的是0。
左移可能会上溢。上溢时有可能会变号。负数进行逻辑右移的话,肯定是会变号的。
位运算符,把两个二进制数每一位的数值进行运算,得出结果。
与(&): 双方都为1时结果才是1,其他情况全是0. 1&1=1; 1%0=0; 0&0=0
非(~):取反用。~1=0; ~0=1
或(|):双方都为0时,结果才是0,其他情况全是1. 0&0=0; 0&1=1; 1&1=1;
异或(^):双方不同时,结果是1,双方相同时,结果是0. 1^1=1; 0^0=1; 0^1=0;
再总结一下对一个已知数进行位运算之后,它会变成什么样:
1、对某一位x进行&1,它会保持原样。&0,它会变成0.对某一位x进行|1,它会变成1。|0,它会保持原样。这四个会非常常用,可以在一些位不变的情况下,把某些位消成0或者补成1。比如,对一个数字进行&11000,只会留下11那两位保留原数字,其他位全都变成0。对一个数字进行|11000,只会留下000三位原数字,其他位都变成1。
2、对某一位x进行~,会取反,但是缺点是,~只能对整个数字进行。灵活度明显不够,但是也是不可或缺的存在。
3、异或的用法大概是用来保留一个数字所有的1位,或者所有的0位。如果^一个全位都是0的数字,结果还是原数字。如果^一个全位是1的数字,结果是原数字全位取反,效果等同于~
然后稍微联想一下这些符号都可以怎样使用,就拿比较常见的RGB颜色取法来作例子吧。
一般我们把RGB颜色存成一个6位的十六进制数,类似0xFF5600。其中FF这两位代表红色的值,56这两位代表绿色的值,00这两位代表蓝色的值。所以我们实际上是有0~255这256(16x16)个值来定义每个颜色。
我们在改颜色的时候,可能只需要改一种颜色,这样调整起来比较方便。比如我想把“绿色”的值增大4,这样颜色看起来就会单纯地“发绿”。那么要如何处理0xFF5600这个数值,让它的“绿色”增加4呢?或者想让绿色的部分颜色值为0,绿色部分颜色值为最大(0xFF)。
int c = 0xFF5600; System.out.println(Integer.toHexString(c)); System.out.println(Integer.toHexString((4<<8)+0xFF5600)); System.out.println(Integer.toHexString(~(0xFF<<8)&0xFF5600)); System.out.println(Integer.toHexString((0xFF<<8)|0xFF5600));
以上代码就可以办到。十六进制的两位,在二进制里相当于八位。所以<<8。其他就比较简单,不详细说了。
输出为:
ff5600
ff5a00
ff0000
ffff00
至此关于位运算的基本内容就都介绍完了,然后列一些很经典的妙用位运算实现的运算。感兴趣的话可以去搜搜找更多。
1、判断整数的奇偶
二进制数取最后一位,就可以判断奇偶。
n&1==1奇数
n&1==0偶数
2、整数的平均值
对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法:
int average(int x, int y) //返回X,Y 的平均值
{
return (x&y)+((x^y)>>1);
}
这个的原理是:
把x和y的所有位拿出来,分成两部分,一部分是x和y的对应位都是1(这样的位,下面简称”全1位“),另一部分是x和y的对应位一个是1一个是0(下面简称”01位“). 对应位全是0的情况,相加还是0,所以不用计算也可以。
对于第一部分,用x&y计算后,所有的全1位都会是1,其他位则全是0.因为实际上我们要求的是 (x的全1位+y的全1位)/2 ,其实就是(某一位的1+对应位的1)/2,结果当然是1.也就是说,全1位相加之后再除以2的结果,是1.这样的话,我们直接x&y算出来的结果,其实就是全1位相加后的平均值。
对于第二部分,用x^y计算的结果,所有01位全是1,其他位全是0. 我们要求的是 (某一位的0+对应位的1)/2 。 也就是说光得到x^y是不够的,这个数还得再除以2,于是用了算术右移1位,相当于除以2.
最后把第一和第二部分一加,得到结果。
3、计算绝对值
int abs( int x )
{
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
}
y = x >> 31得到的一个全0或者全1的32位二进制数。(全0是十进制里的0,全1是十进制里的-1)
(x^y)在x为正时,相当于x,在x为负时,相当于~x。
-y,x为正时,相当于0,x为负时,相当于1
所以以上算法总结之后就是:正数的话直接是原值,负数的话,会取反再+1。会发现其实这是根据原码求补码的过程……
- 讲讲我对Java位运算的理解
- 浅谈我对Java位运算符的了解
- 从源码角度讲讲我对Android和Unity的热更so的理解
- 位运算的理解
- Android四大组件是什么?讲讲你对它们的理解?
- 今天开始,讲讲我和java的故事与事故
- 我对java操作数据库的理解
- 我对java中enum的理解
- 我对Java内存模型的理解
- 我对java和jsp的理解
- 谈谈我对Java中泛型的理解
- 我对java泛型的理解
- 我对java继承的理解
- 我对java线程同步的理解
- 我对JAVA面向对象的理解
- 位运算的小理解
- Java位运算的简单介绍及个人理解!
- Java的位运算
- linux下产看硬件资源的几个常用命令
- 用JSP实现基于Web的RSS阅读器
- C语言:(一)数据类型、运算符和表达式
- Python里的string 和 unicode (一)收藏
- 开始学习使用Game Maker
- 讲讲我对Java位运算的理解
- python的内置函数
- Python里的string 和 unicode (二)收藏
- 九、使用SharedPreferences进行数据存储
- 基于朴素贝叶斯分类器的文本分类算法
- Directfb Flip 参数对性能的影响
- Linux启动文件
- 深入浅出JSONP:解决AJAX跨域问题
- 通过XML创建界面---对象的动态创建以及属性的设置