虽然现在很少用到二进制,可是一些源码中会经常遇到,比如:
//HashMap中的hash方法:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
这里记录一些二进制知识。
一:负数如何显示其二进制。
比如5的二进制数为:00000000 00000000 00000000 00000101(int 类型占用4个字节,每个字节8位).
那么-5的二进制是多少了。对于负数,这里需要理解反码和补码。
1、所谓原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。
2、反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
原码10010= 反码11101 (10010,1为符号码,故为负)
(11101) 二进制= -13 十进制
3、补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
举一例,我们来看整数-5在计算机中如何表示。
假设这也是一个int类型,那么:
1、先取1的原码:00000000 00000000 00000000 00000101
2、得反码: 11111111 11111111 11111111 11111010
3、得补码: 11111111 11111111 11111111 11111011
可以在控制台输出测试:
输出-5的二进制数
System.out.println(Integer.toBinaryString(-5));// 11111111 11111011
二:二进制常用的运算。(^异或运算,与运算&,|运算,左移运算<<,右移运算(有符号)>>,无符号又移>>>)
1.^异或运算。二进制数 相同为0.不同为1;
/**
* ^异或运算。二进制数 相同为0.不同为1;
*/
@Test
public void testOr(){
int first=3;// 0011------------3
int second=4;// 0100------------4
int three=first^second;// 0111------------7
System.out.println(three);//7
}
2.&运算。只有都为1时才为1.否则为0
@Test
/**
* 二进制数 只有都为1时才为1.否则为0
*/
public void testAnd(){
int first=12;//-----------------00001100 值为 12
int second=22;//----------------00010100 值为 22
int three=first&second;//-------00000100 值为 4
System.out.println(three);//---4
}
3.|运算。(参加运算的两个对象只要有一个为1,其值为1。)
@Test //(参加运算的两个对象只要有一个为1,其值为1。)
public void testOrAnd(){
System.out.println(5|2);//7
System.out.println(Integer.toBinaryString(5));// 00000000 00000000 00000000 00000101
System.out.println(Integer.toBinaryString(2));// 00000000 00000000 00000000 00000010
System.out.println(Integer.toBinaryString(7));// 00000000 00000000 00000000 00000111
}
4.左移运算(<<)
如果是整数,直接转为二进制数进行左移,低位补0.
如果是负数。首先也要转为二进制数(通过反码,补码机制)。转移完后,将二进制数加一,取反码。得最终的结果
下面给的例子是 -5<< 结果为20.
/**
* 左移运算(<<) 二进制数向左移动,低位用0补齐
* 反码是除符号位(最高位)外取反
*/
@Test
public void testLeftMove(){
int first=33;// 00100001 值为33
int two=first<<2;// 10000100 值为132
System.out.println(two); //132
//如果是负数
//-5 的二进制,先是算5 的二进制 00000000 00000000 00000000 00000101 然后进行取反(除最高位外)
//-5的反码是 11111111 11111111 11111111 11111010
// 补码 反码+1 11111111 11111111 11111111 11111011
//-----------------------------------计算 -5<<2的值----------------------------
//首先-5的补码左移两位,低位补0得:
// 11111111 11111111 11111111 11101100
//然后 减一得:
// 11111111 11111111 11111111 11101011
//最后取反即可:
// 00000000 00000000 00000000 00010100 值为20
System.out.println(-5<<2);//20
System.out.println(Integer.toBinaryString(-5));//11111111 11111111 11111111 11111011
}
5.右移运算(>>).如果是正数,高位补0.如果是负数,高位补1。
都是先转换为二进制再进行位移运算。如果是负数,也是要借助反码和补码。
/**
* 右移(>>) 二进制数整体向右移动。如果是正数,高位补0.如果是负数,高位补1
*/
@Test
public void testRightMove(){
//-5的二进制 11111111 11111111 11111111 11111011
//右移1位 11111111 11111111 11111111 111111101
System.out.println(-5>>1);//-3
System.out.println(Integer.toBinaryString(-3));//11111111111111111111111111111101
System.out.println(-2147483647>>5);
System.out.println(Integer.toBinaryString(-2147483647>>5));
System.out.println(Integer.toBinaryString(-2147483647));
}
6.无符号的右移运算(>>>)
即无论是正数还是负数,高位都补0.
下面代码可见>>>运算不再采用补码和反码机制了。不管是正数还是负数,最终都是正数。所以直接 将转移后的二进制转换为正数。如果是负数采用>>>的话,也为正数了。
/**
* 无论正负高位同一补0。
*/
@Test
public void testRightMoveThree(){
System.out.println(-5>>>2); //1073741822
System.out.println(Integer.toBinaryString(-5)); //11111111 11111111 11111111 11111011
System.out.println(Integer.toBinaryString(1073741822));// 00111111 11111111 11111111 11111110
}