Java中處理二進制移位
我相信,這篇文章讀起來會相當有趣。
文章中編程語言是Java,用Java的原因:
第一,Java不做數據溢出校驗,這樣我們可以忽略溢出異常;
第二,Java普及率比較高,就像是python或shell,幾乎人人都會吶。
確定一些位運算符:
| 按位或 1001 | 1010 = 1011 (口訣,有真則真 似or邏輯)
^ 按位異或 1001 ^ 1010 = 0011 (口訣,不等則真)
& 按位與 1001 & 1010 = 1000 (口訣,同真則真 似and邏輯)
~ 按位取反 ~1001 = 0110 (這條沒口訣)
>> 右移 或 位移除法
<< 左移 或 位移乘法
以下是一個將十進制數轉化為二進制數顯示為字符串的方法,為了方便我之後的測試而做此轉換方法:
1 public static String integerToBinaryString(int input) { 2 char[] charr = new char[32]; 3 int k = 1; 4 boolean isTrue; 5 for (int i = 32 - 1; i >= 0; i--) { 6 isTrue = (k & input) == k; 7 charr[i] = isTrue ? ‘1‘ : ‘0‘;8 k = k << 1; 9 } 10 return new String(charr); 11 }
此處我沒有使用Integer.toBinaryString(n),是因為這個返回會忽略前面的所有零情況,二進制數據長度很難對齊,給分析帶來很大難度。所以就自己做了integerToBinaryString方法,註意此方法從最低位開始計算每個位的值,因為我這裏用Java寫的測試,而Java是不支持無符號(unsigned)類型數據,有符號和無符號數據在 << 時規則是相同的,但是 >> 時,有符號和無符號會因最高位為符號位的限制,產生一些規則不同的問題,之後做個測試說明。
調用以下方法:
1 int a = 9;// 1001 2 int b = 10;// 1010 3 System.out.println(integerToBinaryString(a | b)); 4 System.out.println(integerToBinaryString(a ^ b)); 5 System.out.println(integerToBinaryString(a & b));
輸出結果:
以上結果用來驗證我說的三個口訣,可見口訣正確。
請用以下代碼進行測試位移:
1 int a = 0x12345678; 2 Scanner scanner = new Scanner(System.in); 3 while (true) { 4 System.out.println(integerToBinaryString(a)); 5 System.out.println(Integer.toHexString(a) + "\t" + a); 6 System.out.print("輸入:"); 7 String in = scanner.next(); 8 if (in.startsWith(">")) { 9 a >>= 1; 10 } else if (in.startsWith("<")) { 11 a <<= 1; 12 } else if (in.startsWith("reset")) { 13 Random rand = new Random(); 14 a = rand.nextInt(); 15 } else { 16 System.out.println("輸入 ‘>‘、‘<‘ 或 ‘reset‘,請繼續..."); 17 } 18 }
控制臺操作:
由上位移可見,當a的最高位為1時(圖片第四步驟),進行>>操作,最高位不會被0取代,繼續進行操作:
通過<<操作將0推到最高位後,然後進行>>操作,最高位會被0覆蓋。
以上簡單的測試,只是為了解釋一下在有符號位情況下,左移和右移操作的稍許不同之處,當然無符號情況下,最高位為1時,進行>>操作,最高位會被0覆蓋。這是為什麽呢?還是找一些官方解釋比較有說服力。
這本教材是我大學裏的必修課程,177頁中位移除法對此有詳細說明。
當然這個說法還是過於粗糙了,當了解了運算器對有符號位運算原理,或許就能豁然開朗了。
我還是說一些比較好玩的東西吧。
補碼和反碼
1 int b = -20; 2 int bb = ~b; 3 int bbb = ~b + 1; 4 System.out.println(integerToBinaryString(b)); 5 System.out.println(integerToBinaryString(bb)); 6 System.out.println(integerToBinaryString(bbb));
補碼bbb和反碼bb
作為反碼好理解點,就是按位翻轉;
補碼這東西總是讓人雲裏霧裏,有點琢磨不透,其實在二進制運算中,補碼就是源碼的模。
二進制中定義:正數補碼是它本身,負數補碼就是它的模了。
有了這層意義,不妨定義一個運算的最大值為13
a=7,b=-2
a+b==? 當然用簡單運算的確能求出值為5
但是我要以以下方法求值
bb == b%13 == 11
這裏要能理解 bb == b,否則很難解釋下去了。
那麽 a+b == a+bb == 7+11 == 18
因此時18大於13,超出部分會溢出,所以a+b == 18%13 == 5
跟我想當然理解 a+b == 7 +(-2) == 7-2 == 5 的值完全吻合
此時,無符號位移位除法空位設置0就很好理解,因為無符號的數一定都是非負數,其補碼就是它本身;有符號位移位除法空位被設置為1估計也就好理解了,是為了方便此負數轉補碼時空位轉為0,當然這只是其中一點原因,也許深刻了解運算器構造和原理,對此問題會有個更好的認識。
Java中處理二進制移位