二进制
进制转换器:
Integer.parseInt(a, "2"); // 2进制
Integer.parseInt(a, "8"); // 8进制
1. 什么是二进制
1.1 10进制
规律:逢10进1
数字:0 1 2 3 4 5 6 7 8 9
权(weight):个 十 百 千 万
基数(base):10
1.2 2进制
规律:逢2进1
数字:0 1
权(weight):32 16 8 4 2 1
基数(base):2
注意进位与退位
六个0的位分别是
32 16 8 4 2 1
0 :000000 --> 0
1 :000001 --> 1
2 :000010 --> 2
3 :000011
4 :000100
5 :000101
6 :000110
7 :000111
8 :001000
··············
43 : 101011 --> 32+0+8+0+2+1 = 43
后面的计算同理通过每个位代表的权来算
88 :000001 011000 --> 64+16+8 = 88
93 :000001 011101 --> 64+16+8+4+1 = 93
666 :001010 011010 --> 2+8+16+128+512 = 666
781 :001100 001101 --> 1+4+8+256+512 = 781
//java1.7以后支持直接书写2进制
//0b开头2进制,0d开头10进制,0o开头8进制,0x开头16进制
int n = 0b110010;
System.out.println(n);
2. 16进制
2进制书写冗长、麻烦、易错
16进制用于简写(缩写)2进制
16进制的基数是2进制的整次幂(16 = 2**4),所以4位2进制可以缩写成为一个16进制数
将二进制从后向前(从低位到高位)每4位数缩写成一个16进制
2进制 16进制
0:0000 0
1:0001 1
2:0010 2
3:0011 3
4:0100 4
5:0101 5
6:0110 6
7:0111 7
8:1000 8
9:1001 9
10:1010 a
11:1011 b
12:1100 c
13:1101 d
14:1110 e
15:1111 f
2进制:0111 0111 1010 0110 0101 1111 1010 1000
16进制: 7 7 a 6 5 f a 8
每四位对应一个16进制数
工作中二进制一般写为16进制
练习
十进制:5853
二进制:0001 0110 1101 1101
16进制: 1 6 d d
3. 补码
计算机中解决负数(有符号数)问题的编码
其核心目的,是将固定位数的二进制数,分一半作为负数
补码是如何将固定位数的2进制作为负数的?
以4位二进制为例讲解补码编码规则
计算时,超过4位数时候会自动溢出舍弃,保持数字始终是4位二进制数
int型是32位补码
一半(最高位为1的数)为负数,一半(最高位为0的数)为正数
-1为:11111111111111111111111111111111 32个1
计算二进制时记得权重和-1
//补码
//int型是32位补码
//一半(最高位为1的数)为负数,一半(最高位为0的数)为正数
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
System.out.println(max);
System.out.println(Integer.toBinaryString(max));
System.out.println(min);
System.out.println(Integer.toBinaryString(min));
//超过了会溢出贯穿,类似于一个圆,回到另外一边,最大值+1得最小值
int max1 = Integer.MAX_VALUE+1;
System.out.println(max1);
System.out.println(Integer.toBinaryString(max1));
int n = -1;
int n1 = 0b11111111111111111111111111111111;//32个1
int n2 = 0xffffffff;
System.out.println(n);
System.out.println(Integer.toBinaryString(n));
System.out.println(n1);
System.out.println(Integer.toBinaryString(n1));
System.out.println(n2);
System.out.println(Integer.toBinaryString(n2));
补码是环形编码
面试题常考
正数溢出得负数,负数溢出得正数,说法不全对,如果正数溢出的超过了一半,他就又是正数了
溢出的范围越大围着数的圈跑的越多,溢出结果可能是负数也可能是正数,依据具体的溢出数来判断
如下可证明
int k = 100;
System.out.println(k+Integer.MAX_VALUE);//溢出为负数
System.out.println(k+Integer.MAX_VALUE+Integer.MAX_VALUE);//溢出为正数
补码的互补对称性
编码结果的巧合
//互补对称
int n = 3;
int n1 = ~3;
System.out.println(n1);
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(n1));
输出结果
-4
00000000000000000000000000000011
11111111111111111111111111111100
3与-4互补对称
根据这个我们可以推出下式(互补对称现象)
-n = ~n+1 ~取反符
一个数的负数等于这个数取反+1(最小值除外)
//验证互补对称公式
int n = 3;
if(-n == ~n+1){
System.out.println("true");
}
//最小值不符合,最小值取反+1还是最小值本身
int m = Integer.MIN_VALUE;
int m1 = ~m+1;
System.out.println(m);
System.out.println(m1);
经典面试题
System.out.println(~5);
//如上代码的输出结果为(D)A.5 B.6 C.-5 D.-6
System.out.println(~-5);
//如上代码的输出结果为(A)A.4 B.5 C.6 D.7
4. 二进制运算
正数计算逢1加该位上的权重,最后加0
负数计算逢0减该位上的权重,最后加-1
运算符号
~ 取反 & 与 | 或 >> 右移位 >>>右逻辑移位 <<左移位
- & 与计算(逻辑乘法)
基本规则 0&0=0 0&1=0 1&0=0 1&1=1 (有0则为0)
计算时需要将两个数字对其位数,对应位置的数字计算与运算
举个例子
n = 01101111 11110000 01101100 10011010
m= 00000000 00000000 00000000 11111111 //掩码
n&m = 00000000 00000000 00000000 10011010
如上计算意义:n&m 是n 的最后8位(掩码计算,也叫拆分计算),8位为1字节,互联网传送的就是一字节为单位
用于互联网信息传输,通过掩码来将信息拆分成字节
**8位掩码(Mask):**1的个数有8个 00000000 00000000 00000000 11111111
int n = 0x7754bff7;
int m = 0xff; //8为掩码(Mask):1的个数有8个
int k = n&m;
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(k));
- ">>>" 逻辑右移位计算
[外链图片转存失败(img-CjPApQua-1563964689056)(C:\Users\Tedu\AppData\Roaming\Typora\typora-user-images\1563870055750.png)]
“k>>>n” 把k向右移位n次,多余的不要
int n = 0x7754bff7;
int n1 = n>>>8; //把n右移位8次
int m = 0xff; //8为掩码(Mask):1的个数有8个
int k = n1&m;
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(k));
通过与计算和移位计算可以把int拆分为字节型
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入一个数:");
int n = input.nextInt();
int m = 0xff;
//System.out.println(Integer.toBinaryString(n));
for(int i=0;i<4;i++){
System.out.print("从右到左第"+(i+1)+"个8位字节存的值:");
System.out.println(Integer.toBinaryString(n>>>(i*8)&m));
}
}
- | 或运算(逻辑加法)
基本规则:有1得1
0|0=0 0|1=1 1|0=1 1|1=1
计算时,将两个数的数位对齐,对应的位数进行或计算
举个例子:
n = 00000000 00000000 00000000 10011101
m = 00000000 00000000 11011111 00000000
n|m = 00000000 00000000 11011111 10011101
如上计算意义:将n和m两个数字进行拼接计算
public static void main(String[] args) {
//或运算
int n = 0x9d;
int m = 0xdf00;
int k = n|m; //拼接
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(m));
System.out.println(Integer.toBinaryString(k));
}
- << 左移位
将2进制数字每个位 向左移动,高位溢出,低位补0
综合应用起来的代码:
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入一个数:");
int n = input.nextInt();
int[] arr = new int[4];//定义一个接收拆分为字节的数组
int n1=0; //定义一个接收合并数的变量
int m = 0xff; //定义8位掩码
//输出显示输入数的二进制
System.out.println("该数的二进制为:"+Integer.toBinaryString(n));
//对该数进行拆分
for(int i=0;i<4;i++){
arr[i] = n>>>(i*8)&m;
System.out.print("从右到左第"+(i+1)+"个8位字节存的值:");
System.out.println(Integer.toBinaryString(arr[i]));
}
//对该数进行合并
for(int i=0;i<4;i++){
n1 += arr[i]<<(8*i);
}
//输出合并后的数
System.out.println("合并后为:"+Integer.toBinaryString(n1));
}
移位计算的数学意义
10进制时候,数字向左移动1次,数值扩大10倍
发现,移动改变的值与权有关
50: 110010.
100: 1100100.
二进制的时候,数字向左移动一次,数值扩大2倍
50: 110010.
25: 11001.
12: 1100. //向小方向取整
二进制的时候,数字向右移动一次,数值缩小两倍,超过的舍弃,小方向取整
">>“与”>>>"的区别:
">>"称为数学右移位计算,当正数时候(高位为0),高位补0;当负数的时候(高位为1),高位补1。其运算结果是数学除法,向小方向取整。
">>>"称为逻辑右移位计算,无论正负高位都补0,负数时候不符合数学运算结果
举个例子;
n = 11111111 11111111 11111111 11001110 // -50
n>>1 = 11111111 11111111 11111111 11100111 // -25
n>>2 = 11111111 11111111 11111111 11110011 // -13
n>>>1 = 01111111 11111111 11111111 11100111 // 比最大值小24
得到结论:
“>>”更接近数学结果:除以2,向小方向取整
">>>"单纯将数位向右移动,其结果不考虑数学意义,进行数字拆分合并时候采取纯正的向右移动
面试案例:
替代2的整数倍乘法时,采用数学移位
n * 32可以替换为 ( n<<5 ) //32为2的5次方
n / 2 (n>=0) 可以替换为 ( n>>1 )