二進制表示法以及Java 移位操作符的介紹


現在系統實現中,加法操作與移位操作運算速度差距不大,但是移位操作在做乘法的時候要快於乘法(減法是變相的加法,除法是變相的乘法)。在一些對運算速度要求高的系統中,移位操作往往能增加不少的效率。

要掌握移位操作符,首先要對二進制有一定的了解。

jdk中計算某一個二進制數之中1的數量的代碼:

public static int bitCount(int i) {
        // HD, Figure 5-2
        i = i - ((i >>> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
        i = (i + (i >>> 4)) & 0x0f0f0f0f;
        i = i + (i >>> 8);
        i = i + (i >>> 16);
        return i & 0x3f;
    }

 

如果不清楚十進制如何轉換成二進制,可以這樣打印出來。

System.out.println(Integer.toBinaryString(2));//結果是10

二進制表示法,我們以int類型的2舉例。運行結果是10,但是實際上它是 0000 0000 0000 0000 0000 0000 0000 0010。

(一般的,我們將左16位作為高位,右16位作為低位)。最高為為符號位,0代表正數,1代表負數

不過如果你打印這樣的二進制。

System.out.println(00000000000000000000000000000010);

運行的結果居然是8!這不是二進制表示法除了問題,也不是編譯器除了問題。編譯器默認將操作數當成十進制做處理,以0開頭的,編譯器都會將它作為八進制處理,以0x開頭就是16進制。

再測試一個 -2。

System.out.println(Integer.toBinaryString(-2));//11111111111111111111111111111110

Java是數值表示是基於二進制補碼的。求一個數的負數,先對所有位求反,0變1,1變0,然后加一。就這個例子來說,先把2的二進制00000000000000000000000000000010 對每一位求反,獲得結果11111111111111111111111111111101然后加1,最后結果是11111111111111111111111111111110。

 

二進制粗略的介紹就到這里,接下來介紹移位操作。

右移一個單位(>>1):往高位插入一個0,其他位都右移(相當於除以2)。左移一個檔位(<<1):低位插入一個0,其他位都左移(相當於乘以2)。<< 和 >> 都是帶符號的操作符,在移位后,如果數值本來是正的,最高位補0,負的就補1。其他的一位操作符還有 >>>,無符號右移操作符,移位后不會去補符號位(可見,這個操作符運算速度要快於有符號的移位操作符)

 

接下來看一個有趣的例子。例子來自Java puzzles。

public static void main(String[] args) {
int i = 0;
while (-1 << i != 0)
i++;
System.out.println(i);
}

按照我們所知道的知識, -1可16進制表示為0xffffffff(二進制為11111111111111111111111111111111)。int 類型的-1是32位都被置位的數值,每一次右移,都在低位插入一個0,第一次變成了:11111111111111111111111111111110,第二次變成了11111111111111111111111111111100…………所以循環了32次然后打印出我們32??但是在運行的時候,我們發現它不打印任何數值,而是進入了一個無限循環。

 

這個問題的謎題就在於-1移動32位的結果不是0,而是返回自身(我並不清楚底層是怎么做到的)。

在移動到 31位的時候:

System.out.println(-1 << 31);//-2147483648
System.out.println(Integer.toBinaryString(-2147483648));//10000000000000000000000000000000

但是移動到32位的時候變回了-1.

System.out.println(-1 << 32);

對於這個問題,我們可以打印一下Integer.MIN_VALUE。它表示int類型的最小數值。

System.out.println(Integer.MIN_VALUE);//-2147483648
System.out.println((-1 << 31) == Integer.MIN_VALUE);//true

我們發現-1左移31位的時候就已經等於 Integer.MIN_VALUE了。我們再比較一下

    int i = -1 << 31 ;
        System.out.println(i << 1);//0
        System.out.println(-1 << 32);//-1
        System.out.println(Integer.MIN_VALUE<<1);//0

這個意思就是,不能一下子丟棄所有的位。

關於移位操作,它還有這樣的規定,對於int 類型,移位只有對低5位作為移動長度,對於long,為6位。(2的5次方32位,2的6次方64位)。而且移位長度是對32取余的,也就是說長度在0到31之間,對於long,在0到63之間。沒有任何的移位方法可以讓一個數值丟棄所有的位。

不過為了打印出 32,我們可以這樣處理。這個循環可以輸出32。

public static void main(String[] args) {
int distance = 0;
for (int val = -1; val != 0; val <<= 1)
distance++;
System.out.println(distance);
}

這個解題思路和前一個不同。前一循環判斷可以一次性移動多少位,而這個循環每移動一位就存儲到變量中。

 

這里還有一個很有趣的問題:

System.out.println(Integer.MIN_VALUE<<1);//-2147483648
        System.out.println(Integer.MAX_VALUE);//2147483647
        System.out.println(Math.abs(Integer.MIN_VALUE));//-2147483648

int最小值的絕對值比最大值大1.而且Math的abs方法對最小值取絕對值還是最小值。 

 

接下來再看一個例子

public static void main(String[] args) {
        int i = -1 ;
        int count = 0;
        while( i != 0){
            i >>>= 1;
//System.out.println(i); count
++; } System.out.println(count); }

接受了上一次的教訓,也許你要脫口而出,無限循環!!!!我們分析這個程序,初始的時候,-1滿足條件進入循環,然后做無符號右移操作.第一次移位操作,-1就變成了2147483647,之后每一次右移就是除以2。把注釋去掉,可以打印如下結果。

2147483647
1073741823
536870911
268435455
134217727
67108863
33554431
16777215
8388607
4194303
2097151
1048575
524287
262143
131071
65535
32767
16383
8191
4095
2047
1023
511
255
127
63
31
15
7
3
1
0

 

接下來再看一段代碼

public static void main(String[] args) {
        short i = -1 ;
        int count = 0;
        while( i != 0){
            i >>>= 1;
//            System.out.println(i);
            count++;
        }
        System.out.println(count);
        
    }

這段代碼和上一段代碼幾乎一模一樣,第一反應是16。short 類型的存儲空間就是16位,再一想就迷糊了,byte,short,char三種類型進行運算的時候自動會轉型成int類型,那么答案是32呢還是16呢。

不過運行這個程序,也許會讓你大吃一驚。居然是一個無限循環!!!

首先在進行移位操作前,short類型的i被提升為int類型進行操作。這樣原來的short 的-1(0xffff)變成了int的-1(0xffffffff)。接下來是無符號的右移,變成了int的 0x7fffffff(這個數字是2147483647)。>>>=是一個混合類型操作,第一步是移位,第二部就要賦值了。於是接下來發生了一個可怕的操作,要把int的數值存儲到short類型的變量中,要進行窄化的原始類型轉換。執行的結果是高位直接被截斷,int的0x7fffffff又變成了short 的-1(0xffff)。於是進入了一個無限循環。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM