1. 程式人生 > >Java中處理二進制移位

Java中處理二進制移位

置0 返回 進制 com 移位 移除 bsp int 說服力

我相信,這篇文章讀起來會相當有趣。

文章中編程語言是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中處理二進制移位