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