进制

二进制

  1. 二进制是计算机采用的表示数字的方式, 每个数位上只有0和1。
  2. 任何整数一定可以采用二进制的方式表示。
  3. 字节内部采用二进制方式记录数字, 一个字节分成八段, 每个分段有一个编号, 最右边分段编号是0, 向左逐渐递增。
    4 相邻分段之间有2倍关系, 某个分段的数字相当于2的次方。

int类型 0~10:

1
2
3
4
5
6
7
8
9
10
11
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010
00000000 00000000 00000000 00000011
00000000 00000000 00000000 00000100
00000000 00000000 00000000 00000101
00000000 00000000 00000000 00000110
00000000 00000000 00000000 00000111
00000000 00000000 00000000 00001000
00000000 00000000 00000000 00001001
00000000 00000000 00000000 00001010

案例:

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String[] args) {
/**
* 2进制演示
*/
int n = 50; //二进制为:110010
System.out.println(n);
//Integer.toBinaryString 将整数在内存中的2进制存储情况转换为字符串,用于显示
System.out.println(Integer.toBinaryString(n));
for(int i=0; i<=100; i++) {
System.out.println(Integer.toBinaryString(i));
}
}

十六进制

  1. 所有数位从右向左每四个数位分成一组, 每组用一个字母替换就得到对应的十六进制表示方式, 先把每组转换成十进制, 转换结果如果在0到9之间则用阿拉伯数字字符替换, 否则用‘a’到’f’这六个英文字母替换。
  2. 每四个数位可以采用8421方法把二进制转化成十六进制。
  3. 可以在程序中使用十六进制表示数字, 但是必须以0x做开头。
  4. %x %X 可以作为十六进制数的占位符(%x输出a-f, %X输出A-F)。
  • 使用二进制的缺点:
    1. 直接书写2进制,繁琐、冗长、易错
    2. 利用16进制缩写2进制,可以达到简化书写的目的.
    3. 缩写规则: 从2进制最低位开始,每4位2进制数缩写为1位16进制数。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) {
/*
* Java 7 开始支持0b开头的2进制直接量
*/
int n = 0b1110110100111110100111111;

/*
* 利用16进制作为2进制的简写形式
*/
n = 0x77d27d77;
System.out.println(Integer.toBinaryString(n));

long l = 0x67abdf122122L;
}

补码(一种处理负数的编码)

什么是补码: 将固定位数2进制数字, 分一半作为负数使用的编码规则。

  • 以4位2进制数为例讲解补码编码规则:
    1. 计算时候保持4位不变,如果超过4位自动溢出。
    2. 将最高位为0的作为正数,最高为1的作为负数,最高位称为符号位。
    3. 由正数倒推设计负数的编码。
    4. 补码是计算底层编码,编程语言将补码转换为10进制与人类沟通。
    5. 补码是环形编码,最大值和最小值相邻。
    6. 由于此编码互补对称(-n = ~n+1),顾称为补码。

案例:补码的最大值和最小值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) {

int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
System.out.println(Integer.toBinaryString(max));
System.out.println(Integer.toBinaryString(min));

long lmax=Long.MAX_VALUE;
long lmin=Long.MIN_VALUE;
System.out.println(Long.toBinaryString(lmax));
System.out.println(Long.toBinaryString(lmin));

int n = -1;
long k = -1L;
System.out.println(Integer.toBinaryString(n));
System.out.println(Long.toBinaryString(k));
}

负数的补码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
/**
* 负数的补码
*/
int n = -1;
System.out.println(Integer.toBinaryString(n));
n = -3;
System.out.println(Integer.toBinaryString(n));
n = -7;
System.out.println(Integer.toBinaryString(n));

for(int i=-100; i<=0; i++) {
System.out.println(Integer.toBinaryString(i));
}
}

先记住-1的补码,然后通过计算一个负数编码 比-1少多少个1 来得到其10进制值

补码的互补对称:

1
2
3
00000000 00000000 00000000 01010000     80   原码
11111111 11111111 11111111 10101111 -81 反码
11111111 11111111 11111111 10110000 -80 补码

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void main(String[] args) {
/**
* 补码的互补对称验证
*/
int n = 80;
int m = ~n+1;
System.out.println(m);
System.out.println(Integer.toBinaryString(n));
System.out.println(Integer.toBinaryString(~n));
System.out.println(Integer.toBinaryString(~n+1));

n = 0x80000000;
m = ~n+1;
System.out.println(m);

}

二进制计算

运算符:

1
2
3
4
5
6
7
8
1. ~ 取反
2. & 与计算机 (逻辑乘法, 有0则0)
- 通过'&'计算结果中存储的是数字n的最后8位数,称为掩码计算。其最终的结果是将 整数n的最后一个字节拆分出来。
3. | 或 (逻辑加法 有1则1)
- 通常将数字n和m拼接在一起
4. >>> 右移位 (将2进制数整体向右移动,低位自动溢出舍弃,高位补0)
5. >> 数学右移位
6. << 左移位 (将2进制数字整体向左移动,高位自动溢出,低位补0)

将一个int拆分为4个byte

示例:

1
2
3
4
5
6
7
8
9
10
11
12
                    b1       b2       b3       b4
n = 01110100 11111101 01001011 10101111

b1=(n>>>24)&0xff 00000000 00000000 00000000 01110100

n>>>16 00000000 00000000 01110100 11111101
b2=(n>>>16)&0xff 00000000 00000000 00000000 11111101

n>>>8 00000000 01110100 11111101 01001011
b3=(n>>>8)&0xff 00000000 00000000 00000000 01001011

b4=n&0xff 00000000 00000000 00000000 10101111

代码:

1
2
3
4
5
6
int n = 0x74fd4baf;
int b1 = (n>>>24) & 0xff;
int b2 = (n>>>16) & 0xff;
int b3 = (n>>>8) & 0xff;
int b4 = n & 0xff;
//验证

<< 左移位

示例:

1
2
3
4
n=         01110111 10101011 10100100 00011111 
m=n<<1 1110111 10101011 10100100 000111110
k=n<<2 110111 10101011 10100100 0001111100
g=n<<8 10101011 10100100 00011111 00000000

用途:将4个byte拼接为int

1
2
3
4
5
6
7
8
9
10
11
b1 =     00000000 00000000 00000000 10110101
b2 = 00000000 00000000 00000000 01101111
b3 = 00000000 00000000 00000000 11101101
b4 = 00000000 00000000 00000000 10111011

b1<<24 10110101 00000000 00000000 00000000
b2<<16 00000000 01101111 00000000 00000000
b3<<8 00000000 00000000 11101101 00000000
b4 00000000 00000000 00000000 10111011
n 10110101 01101111 11101101 10111011
n=(b1<<24)|(b2<<16)|(b3<<8)|b4;

代码:将4个byte合并为一个int

1
2
3
4
5
6
int b1 = 0xb5;
int b2 = 0x6f;
int b3 = 0xed;
int b4 = 0xbb;
int n = (b1<<24) | (b2<<16) | (b3<<8) | b4;
//检查。。。

移位计算的数学意义

10移动小数点计算:

1
2
3
4
十进制数            191321.    
向右移动小数点 1913210. 扩大10倍
向右移动小数点 19132100. 扩大100倍
如果看作小数点不动, 数字向左移动

2进制移位计算,向左移动一次扩大2倍

1
2
3
4
5
6
n =     00000000 00000000 00000000 00110010    50
m=n<<1 0000000 00000000 00000000 001100100 100
k=n<<2 000000 00000000 00000000 0011001000 200
...
a=n>>1 000000000 00000000 00000000 0011001 25
b=n>>2 0000000000 00000000 00000000 001100 12 除以2小方向取整数

>>>>>的区别

  • >>> 逻辑右移位,2进制数字整体向右移动,低位溢出,高位补0 >> 数学右移位,2进制数字整体向右移动,低位溢出,负数补1,正数补0,运算结果相当数学除法,除以2向小方向取整数。

  • >>>>>的区别: >>>就是将数字整体向右移动,不考虑数学结果,适用于单纯数据移位拆分等计算。 >>用于替代数学计算,达到提高运算性能的目的。