1. 程式人生 > 實用技巧 >Unity熱更新04-XLua呼叫C#-011-Lua呼叫C# Unity 協程

Unity熱更新04-XLua呼叫C#-011-Lua呼叫C# Unity 協程

位運算

最高位的bit為1的數都是負數,最高位bit為0的數都是正數。

一:位運算子

1:有符號位移

含義 運算子 示例
左移 << 0011 => 0110
右移 >> 0110 => 0011

2:無符號位移(>>>邏輯位移)

正數的原碼補碼,負數的補碼(符號位不變)原碼取反+1

算數位移
# <<表示左移,不分正負數,低位補0; 

注:以下資料型別預設為byte-8位

左移時不管正負,低位補0
正數:r = 20 << 2
  20的二進位制補碼:0001 0100
  向左移動兩位後:0101 0000
         結果:r = 80

負數:r = -20 << 2
  -20 的二進位制原碼 :1001 0100    符號位 : 0是正數,1是負數
  -20 的二進位制反碼 :1110 1011     反碼:符號位不變,其它位取反
  -20 的二進位制補碼 :1110 1100     補碼:反碼+1

  左移兩位後的補碼:1011 0000       方法1:  補碼 -1 再取反,可得原碼    方法2:求補碼的補碼(就是它的原碼)
        反碼:1010 1111        
        原碼:1101 0000
        結果:r = -80

 ---------------------------------------------------------------------------------------------
>>表示右移,如果該數為正,則高位補0,若為負數,則高位補1;
注:以下資料型別預設為byte-8位

正數:r = 20 >> 2
  20的二進位制補碼:0001 0100
  向右移動兩位後:0000 0101         
       結果:r = 5

負數:r = -20 >> 2
  -20 的二進位制原碼 :1001 0100    負數的原碼:對應正數的原碼符號位取反
  -20 的二進位制反碼 :1110 1011    符號位不變,其餘取反
  -20 的二進位制補碼 :1110 1100    +1
  右移兩位後的補碼:1111 1011    
        反碼:1111 1010
        原碼:1000 0101
        結果:r = -5
   ----------------------------------------------------------------------------------------------

      邏輯位移
      >>>表示無符號右移,也叫邏輯右移,即若該數為正,則高位補0,而若該數為負數,則右移後高位同樣補0

正數: r = 20 >>> 2
    的結果與 r = 20 >> 2 相同;
    
負數: r = -20 >>> 2
注:以下資料型別預設為int 32位
  -20:原始碼:10000000  00000000   00000000   00010100
    反碼:11111111  11111111   11111111   11101011
    補碼:11111111  11111111   11111111   11101100
    右移:00111111  11111111   11111111   11111011
    結果:r = 1073741819
      
      java沒有<<<

3:基本位運算

含義 運算子 示例
按位或 0011
1011
------->
1011 有1為1
按位與 & 0011
1011
------->
0011 全1才為1
按位取反 ~ 0011 => 1100
按位異或(相同為0不同為1) ^ 0011
1011
-------->
1000 相同為0不同為1

4: XOR -異或 運算

異或:相同為 0,不同為 1。也可用“不進位加法”來理解。

異或操作的一些特點:

假設x = 9 1001

操作 示例
x ^ 0 = x 異或0就是x本身 1001
^ 0000
------->
1001 異或:相同為0,不同為1
x ^ 1s = ~x ==>1s = ~0 1001
^ 1111 1s就是全1
-------->
0110 === ~x
x ^ (~x) = 1s 1001
^ 0110
--------->
1111 異或:相同為0,不同為1
x ^ x = 0 1001
^1001
-------->
0000 異或:相同為0,不同為1
c = a ^ b => a ^ c = b, b ^ c = a a = 9.b=7
a: 1001
b: ^ 0111
c: 1110
-----------------------------------
a: 1001
c: ^1110
0111 ===>b=7
a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c // 結合律

5:指定位置的位運算

一條32位的指令,它的最高位是31位,最低位是0位.   以下預設以0開始計。
操作 示例
1. 將x 最右邊的 n 位清零:x& (~0 << n) 假設x:1111 1111
n=4, ~0 =1,左移動4位==》1111 0000
做&運算
1111 1111
1111 0000
1111 0000 ===》將右邊的4位都清零
2. 獲取x 的第 n 位值(0 或者1): (x >> n) & 1 10的二進位制為1010,獲取第1位是1,第2位是0(從低位到高位,從第0位開始數)
1010 --->n =2,右移動2位 ==》10, &1
10
01
00 ===>第二位是0,右數第3位
3. 獲取x 的第 n 位的冪值:x& (1 <<n) x 10: 1010 獲取第3位,n==3, 1<<3 ==> 1000
1010&1000 ===>1000
4. 僅將第 n 位置為1:x|(1 << n) 或運算,有1結果就為1.
5. 僅將第 n 位置為0:x & (~ (1 << n)) 與運算: 有0結果就為0
6. 將x 最高位至第 n 位(含)清零:x& ((1 << n) -1) x:1010 第3位:n==3: 1<<3 ==> 1000 -1 ==> 0111 再&1010
0010 最高位第三位就清零了
7. 將第 n 位至第0 位(含)清零:x& (~ ((1 << (n + 1)) -1))

6:實戰位運算要點

判斷奇偶:

  • x % 2 == 1 —> (x & 1) == 1

  • x % 2 == 0 —> (x & 1) == 0

  • x >> 1 —> x / 2.
    即: x = x / 2; —> x = x >> 1;
    mid = (left + right) / 2; —> mid = (left + right) >> 1;

  • X = X & (X-1) 清零最低位的 1

    x:    1010
    x-1: &1001
    --------------
          1000  將最低位的1清0.
    
  • X & -X => 得到最低位的 1,其它位置就清零了

    最高位的bit為1的數都是負數,最高位bit為0的數都是正數。
    
    x=10:   0000 1010  取反+1  就是負數的補碼
    -x=-10: 1111 0101 + 1==>   1111 0110
     
        &運算:  0000 1010
             &  1111 0110
               0000  0010
        
    
            int x = 10;
            System.out.println("x:"+Integer.toBinaryString(x));
            System.out.println("-x:"+Integer.toBinaryString(-x));
            System.out.println(Integer.toBinaryString(x&-x));
    console:
    x:1010
    -x:11111111111111111111111111110110
    10
    
  • X & ~X => 0

    x: 1010
    ~x:0101
     & 0000
    

二:位運算的應用--leetcode

191. 位1的個數
編寫一個函式,輸入是一個無符號整數,返回其二進位制表示式中數字位數為 ‘1’ 的個數(也被稱為漢明重量)。
public class Solution {
   public int hammingWeight(int n) {
        int count = 0; //統計
        while(n !=0){
            count++;
            n &= (n-1); //清零最低位的1,清理一次,就統計一次
        }

        return count;
    }
}
231. 2的冪
給定一個整數,編寫一個函式來判斷它是否是 2 的冪次方。

這題的關鍵是,理解2的N次冪,只有1個1.
方法1:
class Solution {
    public boolean isPowerOfTwo(int n) {
        if (n == 0) return false;
    while (n % 2 == 0) n /= 2;
    return n == 1;
    }
}

如何獲取二進位制中最右邊的 1:x & (-x)。
如何將二進位制中最右邊的 1 設定為 0:x & (x - 1)。  

方法2:
我們通過 x & (-x) 保留了最右邊的 1,並將其他位設定為 0 若 x 為 2 的冪,則它的二進位制表示中只包含一個 1,則有 x & (-x) = x。
若 x 不是 2 的冪,則在二進位制表示中存在其他 1,因此 x & (-x) != x。

因此判斷是否為 2 的冪的關鍵是:判斷 x & (-x) == x。
class Solution {
  public boolean isPowerOfTwo(int n) {
    if (n == 0) return false;
    long x = (long) n;
    return (x & (-x)) == x;
  }
}
方法3:
2 的冪二進位制表示只含有一個 1。 比如:1 就是1  2:10  4:100    8:1000  都是隻有1個1,才是2的N次冪
x & (x - 1) 操作會將 2 的冪設定為 0,因此判斷是否為 2 的冪是:判斷 x & (x - 1) == 0

class Solution {
  public boolean isPowerOfTwo(int n) {
    if (n == 0) return false;
    long x = (long) n;
    return (x & (x - 1)) == 0;
  }
}

338. 位元位計數
給定一個非負整數 num。對於 0 ≤ i ≤ num 範圍中的每個數字 i ,計算其二進位制數中的 1 的數目並將它們作為陣列返回。
輸入: 5
輸出: [0,1,1,2,1,2]

方法1:
時間複雜度:O(nk)O(nk)。對於每個整數 xx,我們需要 O(k)O(k) 次操作,其中 kk 是 xx 的位數。
空間複雜度:O(n)O(n)。 我們需要 O(n)O(n) 的空間來儲存計數結果。如果排除這一點,就只需要常數空間

class Solution {
    public int[] countBits(int num) {
        int[] ans = new int[num+1];
        for (int i = 0; i <= num; i++) {
             int res = calcOne(i);
             ans[i] = res;
        }
        return ans;

    }

    //計算1的個數
    private int calcOne(int i) {
        int count = 0; //統計
        while (i != 0){
            i = i&(i-1);  //清零最低位的1,清理一次,就統計一次
            count++;
        }
        return count;
    }
}


方法2:DP解法詳情去看leetcode
190. 顛倒二進位制位
顛倒給定的 32 位無符號整數的二進位制位。

public class Solution {
    // you need treat n as an unsigned value
    public int reverseBits(int n) {
        int ans = 0;
        for (int i = 0; i < 32; i++) {
            ans = (ans <<1) + (n&1);  //(n&1) 得到最左邊的數值,ans再左移一位,將最低位新增
            n >>=1; //清除最左邊的一位
        }
        return ans;
    }
}