2进制
2进制
什么是2进制
逢2进1的计数规则
案例:
public class Demo01 {
public static void main(String[] args) {
/*
* 2进制
* - Java 先编译后运行
* - Java 再编译期间,将10进制字面量编译为2进制
* - Java 输出时候将2进制转换为10进制输出
* - Java API Integer.toBinaryString()
* 可以输出2进制内容
* - toBinaryString()输出时候自动省略了高位0
*/
int i = 50; //编译后 110010 运行时候 110010
System.out.println(i);
//输出时候110010 转换为 "50" 再输出
System.out.println(Integer.toBinaryString(i));
//110010
//输出0~200的2进制,研究其值
for(int n = 0; n<=200; n++){
System.out.println(Integer.toBinaryString(n));
}
}
}
0~200的2进制
如何将2进制正数转换为10进制: 将每个1位的权值进行累加
00000000 00000000 00000000 00000000 = 0
00000000 00000000 00000000 00000001 = 1
00000000 00000000 00000000 00000010 = 2
00000000 00000000 00000000 00000011 = 2+1 = 3
00000000 00000000 00000000 00000100 = 4
00000000 00000000 00000000 00000101 = 4+1=5
00000000 00000000 00000000 00000110 = 4+2=6
00000000 00000000 00000000 00000111 = 4+2+1=7
00000000 00000000 00000000 00001000 = 8
00000000 00000000 00000000 00001001 = 8+1 = 9
00000000 00000000 00000000 00001010 = 8+2 = 10
... ...
00000000 00000000 00000000 00100101 = 32+4+1=37
... ...
00000000 00000000 00000000 00101011 = ?
自己动手练习练习: 输出0~200之间的2进制, 随机抽取20个数, 手工计算10进制值,自己编程验证.!
16进制
逢16进1的计数规则
16进制用于缩写2进制, 用于简化2进制!
- 2进制书写非常繁琐, 容易错误
- 利用16进制, 从2进制最低位开始, 每4位缩写为一个16进制数. 缩写后使用方便!
public class Demo02 {
public static void main(String[] args) {
/*
* 2进制书写繁琐
* - Java 7 开始提供了2进制直接量前缀 0b
* - 在数字中使用下划线分割不影响数值
* - 利用16进制缩写,可以简化2进制
* 从最低位开始, 每4位缩写为一位16进制
*/
int n = 100; //1~9开头的数字是10进制直接量
int m = 0b100; //0b开头, 则表示是2进制
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(m));
n = 0b100_11111100_01110001_01011111;
// 4 f c 7 1 5 f
m = 0x4fc715f;
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(m));
//任意编写5组32位2进制数, 利用16进制缩写, 自己输出验证
}
}
8进制
利用8进制可以对2进制进行3位3位缩写,
从2进制最低位开始, 每3位缩写为一位8进制.
- 缩写不算方便, 没有16进制缩写的短
- 8进制数字 0~7
- Java中8进制前缀是 0, 这个经常在考试中出现
如:
int n = 080;
System.out.println(n);
上述代码输出结果 ( D ): A.080 B.80 C.100 D.编译错误
补码(数字编码规则)
什么是补码
补码是计算机中用于处理有符号(正数和负数)数的一种底层编码规则.
- 补码编码思路: 将固定位数的2进制数, 分一半作为负数使用.
- 补码计算时候会自动溢出: 超过固定位数的数字自动舍弃.
利用4位2进制数讨论补码编码原理, 然后推广到32位int类型.
分析: 如何设计 4 位补码
案例:
public class Demo03 {
public static void main(String[] args) {
/*
* 补码的编码规律
* Binary: 2进制
* Integer: 整数
*/
int n = -3;
System.out.println(Integer.toBinaryString(n));
//max 最大 value 值
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
System.out.println(max); // 2147483647
System.out.println(min); // -2147483648
System.out.println(Integer.toBinaryString(max));
System.out.println(Integer.toBinaryString(min));
n = -1;
System.out.println(Integer.toBinaryString(n));
long lmax = Long.MAX_VALUE;
long lmin = Long.MIN_VALUE;
long l = -1L;
System.out.println(Long.toBinaryString(lmax));
System.out.println(Long.toBinaryString(lmin));
System.out.println(Long.toBinaryString(l));
}
}
负数的编码规律
- 首先记住-1的编码
- 研究一个负数(最高位是1),检查这个数比-1少多少
8421
11111111111111111111111111111111 =-1
11111111111111111111111111111101 =-1-2=-3
11111111111111111111111111111001 =-1-2-4=-7
11111111111111111111111111100110 =-1-1-8-16=-26
输出-200到0的编码,随机挑选20个数字, 手工计算负数值, 自己演算结果。
/*
* 研究负数的编码
*/
for(int i=-200; i<0; i++){
System.out.println(Integer.toBinaryString(i));
}
补码的互补对称现象
补码编码结果有一个巧合,正负数互补对称, 因为互补对称, 称作补码。
互补对称公式: -n = ~n + 1, 最小值除外
面试题目1:
System.out.println(~100+1);
上述代码输出结果是( C )
A. -98 B.-99 C.-100 D.-101
面试题目2:
System.out.println(~100);
上述代码输出结果是( D )
A. -98 B.-99 C.-100 D.-101
面试题目3:
System.out.println(~-100);
上述代码输出结果是( B )
A.98 B.99 C.100 D.101
2进制运算
运算符号:
~ 取反
& 与运算
| 或运算
>>> 右移位
>> 数学右移位
<< 左移位
&
与计算
基本运算规则,逻辑乘法:有0则0
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
运算时候, 将两个整数对齐位数, 对应的位进行与运算:
举个例子:
7 7 8 3 6 5 a b
n = 01110111 10000011 01100101 10101011
m = 00000000 00000000 00000000 11111111
k = n&m 00000000 00000000 00000000 10101011
如上案例的意义:k中存储的是数字n的最后8位数。 m是一个用于切分n的一个“掩码(Mask)”,按照1的个数称为 x 位掩码。
程序:
int n = 0x778365ab;
int m = 0xff;
int k = n & m;
//按照2进制输出 n m k
>>>
右移位运算
运算规则: 将2进制数位整体向右移动,地位自动溢出,高位补0
举个例子:
n = 01111110 00010111 01111100 01110111
m = n>>>1 001111110 00010111 01111100 0111011
k = n>>>2 0001111110 00010111 01111100 011101
g = n>>>8 00000000 01111110 00010111 01111100
b3= (n>>>8) & 0xff;
程序:
int n = 0x7e177c77;
int m = n>>>1;
int k = n>>>2;
int g = n>>>8;
int b1 = (n>>>24) & 0xff;
int b2 = (n>>>16) & 0xff;
int b3 = (n>>>8) & 0xff;
int b4 = n & 0xff;
//按照2进制输出 n m k g b3
|
或运算
运算规则,逻辑加法, 有1则1:
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
运算时候将两个2进制数对齐位,对应的位进行或运算:
n = 00000000 00000000 00000000 10111011
m = 00000000 00000000 11110111 00000000
k = n|m 00000000 00000000 11110111 10111011
上述案例的意义:错位合并两个8位数!
案例:
int n = 0xbb;
int m = 0xf700;
int k = n|m;
//检查 n m k
<<
左移位运算
规则: 将2进制数整体向左移位,移动以后高位溢出, 低位补0
例子:
n = 01011101 11111110 00001010 10111010
m = n<<1 1011101 11111110 00001010 101110100
k = n<<2 011101 11111110 00001010 1011101000
案例:
int n = 0x5dfe0aba;
int m = n<<1;
int k = n<<2;
移位运算是数学意义
2进制时候,数字整体向左移动一次, 数值扩大2倍。
案例:
int n = 5;
System.out.println(n<<1); //10
System.out.println(n<<2); //20
System.out.println(n<<3); //40
右移位的区别
>>>
逻辑右移位运算, 无论正负,低位自动溢出,高位补0, 仅仅在逻辑上将数字向右移动, 不关心是否是数学除以2的结果。
>>
数学右移位运算,移位时候低位自动溢出, 正数时候高位补0, 负数时候高位补1,结果是数学除以2,向小反向取整数的结果
举个例子,负数时候:
n = 11111111 11111111 11111111 11001110 =-1-1-16-32=-50
m=n>>1 111111111 11111111 11111111 1100111 =-1-8-16=-25
k=n>>2 1111111111 11111111 11111111 110011 =-1-4-8=-13
g=n>>>1 011111111 11111111 11111111 1100111 =?
用法:
- 如果用于替代 数学除以2 时候使用
>>
- 如果用于将2进制数整体向右移位,不关心数学结果,使用
>>>
经典面试题目:
- 将n * 8 可以替换(优化)为 ( n<<3 )
- 将n / 2 可以替换(优化)为 ( n>>1 )
拆分合并整数
为何需要对整数进行拆分合并:
- 互联网按照字节(8位)为单位传输数据
- 任何数据都需要拆分为 字节(8位) 数据进行传输: 编码
- 整数:32位需要拆分为 4字节
- long:64位需要拆分为 8字节
- ... ...
- 收到数据时候, 再将字节组合为数据:解码
整数的拆分(编码):
b1 b2 b3 b4
n = 01111011 11110000 11011100 00111101
b1 = 00000000 00000000 00000000 01111011
b2 = 00000000 00000000 00000000 11110000
b3 = 00000000 00000000 00000000 11011100
b4 = 00000000 00000000 00000000 00111101
int b1 = (n>>>24) & 0xff;
int b2 = (n>>>16) & 0xff;
int b3 = (n>>>8) & 0xff;
int b4 = n & 0xff;
整数的解码(合并):自己编写代码进行测试
b1 = 00000000 00000000 00000000 01111011
b2 = 00000000 00000000 00000000 11110000
b3 = 00000000 00000000 00000000 11011100
b4 = 00000000 00000000 00000000 00111101
n = 01111011 11110000 11011100 00111101
b1<<24 01111011 00000000 00000000 00000000
b2<<16 00000000 11110000 00000000 00000000
b3<<8 00000000 00000000 11011100 00000000
b4<<0 00000000 00000000 00000000 00111101
n = (b1<<24) | (b2<<16) | (b3<<8) | (b4<<0)
作业:该你自己动手啦
将一个 long 类型整数拆分为 8个字节。
将8个 字节合并为一个long 类型整数。
检查long类型的最大值,最小值和-1,-3