2019年8月15日 星期四

java 的 ~、^、<<、>>、>>>、<<=、>>=、>>>=



final byte x = 40;
final byte y = -40;
    
System.out.println(~x); // -41
System.out.println(~y); // 39
    
System.out.println(5 ^ 6); // 3
System.out.println(x ^ y); // -16
System.out.println(Integer.toBinaryString(x)); // 101000
System.out.println(Integer.toBinaryString(y)); // 11111111111111111111111111011000
System.out.println(Integer.toBinaryString(-16)); // 11111111111111111111111111110000
    
System.out.println(x << 2); // 160
System.out.println(x >> 2); // 10
System.out.println(x >>> 2); // 10
    
System.out.println(y << 2); // -160
System.out.println(y >> 2); // -10
System.out.println(y >>> 2); // 1073741814
System.out.println(y >>> 4); // 268435453

※這裡的符號都會轉換成二進制,可用 Integer.toBinaryString 或 Long.toBinaryString 查看,這兩個差在 32 位和64 位


40 的二進制:(前面沒有視同 0,總共 32 或 64 位) 10 1000
-40 的二進制:1111 1111 1111 1111 1111 1111 1101 1000


※正二進制轉負二進制

二進制取反 +1
如:-40 的二進制就是 40 的二進制取反 +1
40 -> 10 1000 取反 --> 01 0111 加 1 -> (前面很多1)  01 1000

※負的二進制轉成 10 進制

取反後 +1 的 10 進制乘 -1

※~ 取反(簡單公式就是 (x+1)*-1)

正數:二進制+1後轉10進制,然後乘-1
負數:二進制取反
.40 轉二進制 -> 10 1000 -> 10 1001 = 41 * -1 -> -41

.-40 轉二進制 -> (前面很多1) 01 1000 取反->10 0111 = 39



※ ^ XOR 互相排斥,一正一反為 true,兩正兩反為 false

.5 ^ 6
5 -> 101
6-> 110
XOR 後,為 011 -> 3

.40 ^ -40
40 -> 101000
-40 -> 1111 1111 1111 1111 1111 1111 1101 1000
XOR 後,為 (前面很多1) 11 0000

驗證:
(前面很多1) 11 0000 取反 -> 1111 + 1 -> 1 0000 -> 16 * -1 = -16

※有兩個數想要互換,但不能用中間的 temp 變數,有以下兩種方法

一個數對另一個數互斥兩次,值不會變
var x = 66;
var y = 77;
x = x ^ y;
y = x ^ y;
x = x ^ y;
要小心 x 和 y 的值一樣時,不能用這招,直接 return 即可
----------------------------------
先取得兩數的和再進行減法運算
var x = 66;
var y = 77;
x = x + y;
y = x - y;
x = x - y;

要小心 x+y 超過類型的範圍就不行了


※ << 左移運算符

.40 -> 10 1000
<< 2 往左二位,就相當於在最右邊增加 2 個 0
1010 0000 -> 2 的 7 次方 + 2 的 5 次方 -> 128 + 32 = 160
以十進位來說, <<2 就是乘 2 的 2 次方;<<3 就是乘 2 的 3 次方

.-40 ->  (前面很多1)  01 1000 -> 0110 0000

驗證:
(前面很多1) 0110 0000 取反 -> 1001 1111 + 1 -> 1010 0000 -> 32 + 128 -> 160 * -1 = -160


可以背快速的用法:
例一:40 << 4:4 表示 2 的 4 次方,結果為 40 *16
例二:1 << 3:1 * 8

※ >> 右移運算符

.40 -> 10 1000
>> 2 往右二位,就相當於最右邊刪除 2 位,如果是正數,最左邊補 2 個 0
1010 = 10
以十進位來說, >>2 就是除 2 的 2 次方;>>3 就是除 2 的 3 次方,如果有小數點都是無條件捨去

.-40 -> (前面很多1)  01 1000 ->  (前面很多1) 0110,負數最左邊是補 1

驗證:
(前面很多1) 0110 取反 -> 1001 + 1 -> 1010 = 10 * -1 = -10


※左、右移運算符的應用可看這篇




※ >>> 無符號右移運算符 (就是只有正數,正數結果和 >> 一樣)

※就算宣告成 byte,結果還是 32 位,也不會報錯,可以正常使用

※因為只有正數,左邊一定是 0,至於有幾個 0,要看 >>> 3 給 3 那就是 3 個 0;右邊的處理和 >> 一樣,刪 3 個最右邊的

※>> 右移運算符的負數是最左邊補 1,無符號是補 0,最左邊補0後,換算時就不是負的了,所以說只有正數,不用再取反+1了

-40 -> (前面很多1)  01 1000

.>>> 2,就相當於最右邊刪除 2 位,最左邊 2 個改 0
‭0011 1111 1111 1111 1111 1111 1111 0110‬
最左邊的 0 可以刪除


.>>> 4,就相當於最右邊刪除 4 位,最左邊 4 個改 0
‭0000 1111 1111 1111 1111 1111 1111 1101
最左邊的 0 可以刪除‬,最下面小算盤的圖就是省略了

從最右邊的 2的0次方到最左邊的 2 的 31 次方,一個一個加起來就是 10 進位了,但這個用人工算太累了,可用小算盤,如下:




※<<=、>>=、>>>=


int x = 40 << 2;

int o = 40;
o <<= 2; // o = o << 2;

此時 x 和 o 是一樣的意思,一定要分兩行,否則編譯錯誤,右移和無符號右移也是一樣,這在 jdk7 的 HashMap 原碼看到的