二进制你了解多少?

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

上一篇:java精度损失


下一篇:Windows内核开发-6-内核机制 Kernel Mechanisms