java位运算总结
来源:互联网 发布:linux 进程 流量 编辑:程序博客网 时间:2024/06/05 08:35
位运算因为是CPU直接支持的操作指令,也是基于二进制的操作,所以具有相当高的效率,在一些场合,合理应用位运算将具有很高的性能。通常在一些加密算法,图型算法中都会使用到位运算。
Java 位运算符
位运算符用来对二进制位进行操作 ,Java中提供了如下所示的位运算符 :
位运算符 (>>,<<,>>>,&,|,^,~ ) ,位运算符中 ,除~以外 ,其余均为二元运算符。操作数只能为整型和字符型数据。
1. 基础知识
补码
所有的整数类型(除了char类型之外)都是有符号的整数。这意味着他们既能表示正数,又能表示负数。 Java使用补码来表示二进制数 ,在补码表示中 ,最高位为符号位 ,正数的符号位为 0,负数为 1。补码的规定如下 :
对正数来说 ,最高位为 0,其余各位代表数值本身 (以二进制表示 ),如 +42的补码为 00101010。
对负数而言 ,把该数绝对值的补码按位取反 ,然后对整个数加 1,即得该数的补码。如 -42的补码为 11010110 (00101010按位取反 11010101 +1=11010110 )
用补码来表示数 ,0的补码是唯一的 ,都为 00000000。 (而在原码 ,反码表示中 ,+0和 -0的表示是不唯一的 ,可参见相应的书籍 )。而且可以用 111111表示 -1的补码 (这也是补码与原码和反码的区别 )。
*类型长度
整型
整型常量在机器中占 32位 ,具有 int型的值 ,对于 long型值 ,则要在数字后加 L或 l,如 123L表示一个长整数 ,它在机器中占 64位。整型变量的类型有 byte、 short、 int、 long四种。下面列出各类型所在内存的位数和其表示范围。
数据类型描述所占位数
Integersbyte Byte-length integer 8-bit two‘s complement
shortShort integer 16-bit two‘s complement
intInteger 32-bit two‘s complement
longLong integer 64-bit two‘s complement
Realnumbers
floatSingle-precision floating point 32-bit IEEE 754
doubleDouble-precision floating point 64-bit IEEE 754
Othertypes
char Asingle character 16-bit Unicode character
boolean Aboolean value (true or false) true or false
int类型是最常使用的一种整数类型。它所表示的数据范围足够大 ,而且适合于 32位、64位处理器。但对于大型计算 ,常会遇到很大的整数 ,超出 int类型所表示的范围 ,这时要使用long类型。
由于不同的机器对于多字节数据的存储方式不同 ,可能是从低字节向高字节存储 ,也可能是从高字节向低字节存储 ,这样 ,在分析网络协议或文件格式时 ,为了解决不同机器上的字节存储顺序问题 ,用 byte类型来表示数据是合适的。而通常情况下 ,由于其表示的数据范围很小 ,容易造成溢出 ,应避免使用。
short类型则很少使用 ,它限制数据的存储为先高字节 ,后低字节 ,这样在某些机器中会出错。
整型变量的定义 ,如 :
byte b;//指定变量b为byte型
short s;//指定变量s为short型
int i; //指定变量i为int型
long l;//指定变量l为long型
浮点型 (实型 )数据
实型变量的类型有 float和 double两种 ,下表列出这两种类型所占内存的位数和其表示范
围。
数据类型所占位数数的范围
float 323.4e-038~3.4e+038
double 641.7e-308~1.7e+308
双精度类型 double比单精度类型 float具有更高的精度和更大的表示范围 ,常常使用。
(三)实型变量定义 ,如
float f;//指定变量 f为 float型
double d;//指定变量 d为 double型
[注 ]与 C、 C++不同 ,Java中没有无符号型整数 ,而且明确规定了整型和浮点型数据所占的
内存字节数 ,这样就保证了安全性、鲁棒性和平台无关性。
2.Java 位运算符
Java 定义的位运算(bitwise operators)直接对整数类型的位进行操作,这些整数类型包括long,int,hort,char,and byte 。表4-2列出了位运算:
运算符 结果
~ 按位非(NOT)(一元运算)
& 按位与(AND)
| 按位或(OR)
^ 按位异或(XOR)
>> 右移
>>>右移,左边空出的位以0填充;无符号右移
<< 左移
&= 按位与赋值
|=按位或赋值
^= 按位异或赋值
>>= 右移赋值
>>>=右移赋值,左边空出的位以0填充;无符号左移
<<=左移赋值
3. 详细解释
1.按位非(NOT)
按位非也叫做补,一元运算符NOT“~”是对其运算数的每一位取反。例如,数字42,它的二进制代码为:
00101010
经过按位非运算成为
11010101
2.按位与(AND)
按位与运算符“&”,如果两个运算数都是1,则结果为1。其他情况下,结果均为零。看下面的例子:
0010101042 &00001111 15
0000101010
3.按位或(OR)
按位或运算符“|”,任何一个运算数为1,则结果为1。如下面的例子所示:
0010101042 | 00001111 15
0010111147
4. 按位异或(XOR)
按位异或运算符“^”,只有在两个比较的位不同时其结果是 1。否则,结果是零。下面的例子显示了“^”运算符的效果。这个例子也表明了XOR运算符的一个有用的属性。注意第二个运算数有数字1的位,42对应二进制代码的对应位是如何被转换的。第二个运算数有数字0的位,第一个运算数对应位的数字不变。当对某些类型进行位运算时,你将会看到这个属性的用处。
0010101042 ^ 00001111 15
0010010137
位逻辑运算符的应用
下面的例子说明了位逻辑运算符:
// Demonstrate the bitwise logicaloperators.
class BitLogic {
public static void main(String args[]) {
String binary[] = {"0000","0001", "0010", "0011", "0100","0101", "0110", "0111", "1000","1001", "1010", "1011", "1100","1101", "1110", "1111"
};
int a = 3; // 0 + 2 + 1 or 0011 in binary
int b = 6; // 4 + 2 + 0 or 0110 in binary
int c = a | b;
int d = a & b;
int e = a ^ b;
int f = (~a & b) | (a & ~b);
int g = ~a & 0x0f;
System.out.println(" a = " +binary[a]);
System.out.println(" b = " + binary[b]);
System.out.println(" a|b = " + binary[c]);
System.out.println(" a&b = " + binary[d]);
System.out.println(" a^b = " + binary[e]);
System.out.println("~a&b|a&~b = " + binary[f]);
System.out.println(" ~a = " + binary[g]);
}
}
在本例中,变量a与b对应位的组合代表了二进制数所有的 4 种组合模式:0-0,0-1,1-0,和1-1 。“|”运算符和“&”运算符分别对变量a与b各个对应位的运算得到了变量c和变量d的值。对变量e和f的赋值说明了“^”运算符的功能。字符串数组binary代表了0到15对应的二进制的值。在本例中,数组各元素的排列顺序显示了变量对应值的二进制代码。数组之所以这样构造是因为变量的值n对应的二进制代码可以被正确的存储在数组对应元素binary[n]中。例如变量a的值为3,则它的二进制代码对应地存储在数组元素binary[3]中。~a的值与数字0x0f(对应二进制为0000 1111)进行按位与运算的目的是减小~a的值,保证变量g的结果小于16。因此该程序的运行结果可以用数组binary对应的元素来表示。该程序的输出如下:
a = 0011 b = 0110 a|b = 0111 a&b = 0010a^b = 0101 ~a&b|a&~b = 0101 ~a = 1100
5.
左移运算符
左移运算符<<使指定值的所有位都左移规定的次数。它的通用格式如下所示:
value << num
这里,num指定要移位值value移动的位数。也就是,左移运算符<<使指定值的所有位都左移num位。每左移一个位,高阶位都被移出(并且丢弃),并用0填充右边。这意味着当左移的运算数是int类型时,每移动1位它的第31位就要被移出并且丢弃;当左移的运算数是long类型时,每移动1位它的第63位就要被移出并且丢弃。
在对byte 和short类型的值进行移位运算时,你必须小心。因为你知道Java在对表达式求值时,将自动把这些类型扩大为 int型,而且,表达式的值也是int型。对byte 和short类型的值进行移位运算的结果是int型,而且如果左移不超过31位,原来对应各位的值也不会丢弃。但是,如果你对一个负的byte或者short类型的值进行移位运算,它被扩大为int型后,它的符号也被扩展。这样,整数值结果的高位就会被1填充。因此,为了得到正确的结果,你就要舍弃得到结果的高位。这样做的最简单办法是将结果转换为 byte型。下面的程序说明了这一点:
// Left shifting a byte value.
class ByteShift {
public static void main(String args[]) {
byte a = 64, b;
int i;
i = a << 2;
b = (byte) (a << 2);
System.out.println("Original value of a: " + a);
System.out.println("i and b: " + i + " " + b);
}
}
该程序产生的输出下所示:
Original value of a: 64
i and b: 256 0
因变量a在赋值表达式中,故被扩大为int型,64(0100 0000)被左移两次生成值256(10000 0000)被赋给变量i。然而,经过左移后,变量b中惟一的1被移出,低位全部成了0,因此b的值也变成了0。
既然每次左移都可以使原来的操作数翻倍,程序员们经常使用这个办法来进行快速的2的乘法。但是你要小心,如果你将1移进高阶位(31或63位),那么该值将变为负值。下面的程序说明了这一点:
// Left shifting as a quick way to multiplyby 2.
class MultByTwo {
public static void main(String args[]) {
int i;
int num = 0xFFFFFFE;
for(i=0; i<4; i++) {
num = num << 1;
System.out.println(num);
}
}
该程序的输出如下所示:
536870908
1073741816
2147483632
-32
初值经过仔细选择,以便在左移 4位后,它会产生-32。正如你看到的,当1被移进31位时,数字被解释为负值。
6.
右移运算符
右移运算符>>使指定值的所有位都右移规定的次数。它的通用格式如下所示:
value >> num
这里,num 指定要移位值value 移动的位数。也就是,右移运算符>>使指定值的所有位都右移num位。下面的程序片段将值32右移2次,将结果8赋给变量a:
int a = 32;
a = a >> 2; // a now contains 8
当值中的某些位被“移出”时,这些位的值将丢弃。例如,下面的程序片段将35右移2次,它的2个低位被移出丢弃,也将结果8赋给变量a:
int a = 35;
a = a >> 2; // a still contains 8
用二进制表示该过程可以更清楚地看到程序的运行过程:
00100011 35
>> 2
00001000 8
将值每右移一次,就相当于将该值除以2并且舍弃了余数。你可以利用这个特点将一个整数进行快速的2的除法。当然,你一定要确保你不会将该数原有的任何一位移出。
右移时,被移走的最高位(最左边的位)由原来最高位的数字补充。例如,如果要移走的值为负数,每一次右移都在左边补1,如果要移走的值为正数,每一次右移都在左边补0,这叫做符号位扩展(保留符号位)(sign extension ),在进行右移操作时用来保持负数的符号。例如,–8 >> 1是–4,用二进制表示如下:
11111000 –8 >>1 11111100 –4
一个要注意的有趣问题是,由于符号位扩展(保留符号位)每次都会在高位补1,因此-1右移的结果总是–1。有时你不希望在右移时保留符号。例如,下面的例子将一个byte型的值转换为用十六进制表示。注意右移后的值与0x0f进行按位与运算,这样可以舍弃任何的符号位扩展,以便得到的值可以作为定义数组的下标,从而得到对应数组元素代表的十六进制字符。
// Masking sign extension.
class HexByte {
static public void main(String args[]) {
char hex[] = {
’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,
’8’, ’9’, ’a’, ’b’, ’c’, ’d’, ’e’, ’f’’
};
byte b = (byte) 0xf1;
System.out.println(“b = 0x” + hex[(b >> 4) & 0x0f] + hex[b & 0x0f]);
}
}
该程序的输出如下:
b = 0xf1
7.
无符号右移
正如上面刚刚看到的,每一次右移,>>运算符总是自动地用它的先前最高位的内容补它的最高位。这样做保留了原值的符号。但有时这并不是我们想要的。例如,如果你进行移位操作的运算数不是数字值,你就不希望进行符号位扩展(保留符号位)。当你处理像素值或图形时,这种情况是相当普遍的。在这种情况下,不管运算数的初值是什么,你希望移位后总是在高位(最左边)补0。这就是人们所说的无符号移动(unsigned shift )。这时你可以使用Java的无符号右移运算符>>>,它总是在左边补0。
下面的程序段说明了无符号右移运算符>>>。在本例中,变量a被赋值为-1,用二进制表示就是32位全是1。这个值然后被无符号右移24位,当然它忽略了符号位扩展,在它的左边总是补0。这样得到的值255被赋给变量a。
int a = -1; a = a >>> 24;
下面用二进制形式进一步说明该操作:
11111111 11111111 11111111 11111111 int型-1的二进制代码>>> 24无符号右移24位00000000 00000000 00000000 11111111 int型255的二进制代码
由于无符号右移运算符>>>只是对32位和64位的值有意义,所以它并不像你想象的那样有用。因为你要记住,在表达式中过小的值总是被自动扩大为int型。这意味着符号位扩展和移动总是发生在32位而不是8位或16位。这样,对第7位以0开始的byte型的值进行无符号移动是不可能的,因为在实际移动运算时,是对扩大后的32位值进行操作。下面的例子说明了这一点:
// Unsigned shifting a byte value.
class ByteUShift {
static public void main(String args[]) {
int b = 2;
int c = 3;
a |= 4;
b >>= 1;
c <<= 1;
a ^= c;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
}
}
该程序的输出如下所示:
a = 3
b = 1
c = 6
8.
对于java移位运算的总结:
1. 对于左移运算,每左移一个位,高阶位都被移出(并且丢弃),并用0填充右边。这意味着当左移的运算数是int类型时,每移动1位,它的第31位就要被移出并且丢弃;当左移的运算数是long类型时,每移动1位它的第63位就要被移出并且丢弃。
2. 左移都可以使原来的操作数翻倍,程序员们经常使用这个办法来进行快速的2的乘法。但是你要小心,如果你将1移进高阶位(31或63位),那么该值将变为负值。
3. 在对byte和short类型的值进行移位运算时 , Java将自动把这些类型扩大为 int型,而且,移位后的值也是int型;如果左移不超过31位,原来对应各位的值不会丢弃。但是,如果你对一个负的byte或者short类型的值进行移位运算,它被扩大为int型后,它的符号也被扩展,结果的高位就会被1填充。因此,为了得到正确的结果,你就要舍弃得到结果的高位。这样做的最简单办法是将移位运算的结果再转换成byte型。
4. 每右移一次,就相当于将该值除以2并且舍弃了余数。你可以利用这个特点将一个整数进行快速的2的除法。当然,你一定要确保你不会将该数原有的任何一位移出。
5. 无符号右移(>>>)与右移的区别:
(1)每一次右移,>>运算符总是自动地用它的先前最高位的内容补它的最高位。这样做保留了原值的符号
(2)无符号移动总是在高位(最左边)补0。
6. 与C、C++不同,Java中没有无符号型整数,而且明确规定了整型和浮点型数据所占的内存字节数,这样就保证了安全性、鲁棒性和平台无关性。
9.
其他:
(1)BCD码(二到十进制编码)
人们通常习惯使用十进制数,而计算机内部多采用二进制表示和处理数值数据,因此在计算机输入和输出数据时,就要进行由十进制到二进制的转换处理。
把十进制数的每一位分别写成二进制形式的编码,称为二进制编码的十进制数,即二到十进制编码或BCD(Binary Coded Decimal)编码。
BCD码编码方法很多,通常采用8421编码,这种编码方法最自然简单。其方法使用四位二进制数表示一位十进制数,从左到右每一位对应的权分别是23、22、21、20,即8、4、2、1。例如十进制数1975的8421码可以这样得出
1975(D)=0001 1001 0111 0101(BCD)
用四位二进制表示一位十进制会多出6种状态,这些多余状态码称为BCD码中的非法码。BCD码与二进制之间的转换不是直接进行的,当需要将BCD码转换成二进制码时,要先将BCD码转换成十进制码,然后再转换成二进制码;当需要将二进制转换成BCD码时,要先将二进制转换成十进制码,然后再转换成BCD码。
10.
(2)字符编码
在计算机中,对非数值的文字和其他符号进行处理时,首先要对其进行数字化处理,即用二进制编码来表示文字和符号。字符编码就是以二进制的数字来对应字符集的字符,目前用得最普遍的字符集是ANSI,对应ANSI字符集的二进制编码就称为ANSI码,DOS和Windows系统都使用了ANSI码。在输入过程中,系统自动将用户输入的各种数据按编码的类型转换成相应的二进制形式存入计算机存储单元中;在输出过程中,再由系统自动将二进制编码数据转换成用户可以识别的数据格式输出给用户。
(3)ASCⅡ码
用七位二进制表示字符的一种编码,使用一个字节表示一个特殊的字符,字节高位为0或用于在数据传输时的校验。附表为标准的ASCⅡ码表。
- Java位运算总结
- java位运算总结
- java位运算总结
- Java位运算符总结
- Java位运算符总结
- 【转载】Java位运算总结
- java位运算符总结
- Java 位运算符总结
- Java 位运算和运算符总结
- Java位运算总结:位运算用途广泛
- Java位运算总结:位运算用途广泛
- Java位运算总结:位运算用途广泛
- Java位运算总结:位运算用途广泛
- Java位运算总结:位运算用途广泛
- Java位运算总结:位运算用途广泛
- Java语言中的位运算及总结
- 黑马程序员 java中的位运算总结
- Java位运算小知识总结
- 升级 ADT 时Could not find D:\Android\a...sdk-windows\tools\adb.exe
- 苹果面试8大难题及答案
- spring3.x + activemq学习笔记(一)
- Python中dict详解
- 阿里巴巴2014年实习生笔试题目
- java位运算总结
- 10大知名opengl教程
- 详解java序列化(二)
- TCP拥塞控制算法 优缺点 适用环境 性能分析
- 数据库为何要建立索引的原因说明
- 阿里面试 事件委托
- angular与datatables 的结合(二)
- linux环境下java swing中文乱码
- 忆苦思甜之飘香猪尾