Java中的位運算機制詳解

今天在讀Think in Java的時候,讀到位運算的地方有這麼一段話不理解:

“對char,byte或者short進行移位處理,那麼在移位進行之前,它們會自動轉換成一個int。只有數值右側的5個低位纔會有用。這樣可防止我們在一個int數裏移動不切實際的位數。若對一個long值進行處理,最後得到的結果也是long。此時只會用到右側的6個低位,防止移動超過long值裏現成的位數。”

剛看到這句話特別不理解爲什麼char,byte,short,進行移位處理的時候只有數值右端的低五位纔有用?
後來查閱資料後才發覺應該是翻譯錯誤,讓自己理解出了偏差,這句話其實應該是這樣的:對char,byte或者short進行移位處理,那麼在移位進行之前,它們會自動轉換成一個int。只有右側數值的5個低位纔會有用。翻譯把右側數值翻譯成了數值右側,讓我們的思維進入了誤區,其實這句話跟上一句話沒有直接聯繫。
對char,byte或者short進行移位處理,那麼在移位進行之前,它們會自動轉換成一個int。 這是獨立的一句話,意思說這三個類型進行移位運算的時候會選轉換成int,並且值也會存在int裏面。
而後面一句話:只有右側數值的5個低位纔會有用。
理解這句話我們需要知道位運算操作符的格式:
格式:左側數字 - 移位運算符 - 右側數字 —— 把左側數字的二進制移動右側數字的位數
例如: 10 >> 3 表示10 帶符號右移3位
弄懂規則就好理解我們的疑惑了:爲什麼只有右側數值的5個低位纔會有用?
首先:int 所佔的最大內存是32位,那麼如果我們把一個int類型的數字移32位,那麼就會移出原本數字的所有位數,這樣是沒有意義的,那麼我們就要保證移位不會超過32位,那我們就取右側數字的低五位(最大值爲31)來保證我們的移位操作不會移動該類型最大的位數。
我們可以用代碼來驗證這句話:

public static void main(String[] args) {
        //定義目標數字用於移位操作
        int a = 1000000;
        //我想要移動的位數,即移位符號右側數字
        short b = 50;

        //令c = a 無符號右移50位
        int c = (a >>> b);
        //18是50轉換爲二進制後取低五位的值
        int d = a >>> 18;

        System.out.println(c);
        System.out.println(d);

    }

測試代碼發現c 與 d的值相同,證明我們的結論是正確的。下面是我在網上找的一點拓展知識:

引用網上內容:
請用最有效率的方法計算出2乘以8等於幾?

這裏所謂的最有效率,實際上就是通過最少、最簡單的運算得出想要的結果,而移位是計算機中相當基礎的運算了,用它來實現準沒錯了。左移位“<<”把被操作數每向左移動一位,效果等同於將被操作數乘以2,而2*8=(2*2*2*2),就是把2向左移位3次。因此最有效率的計算2乘以8的方法就是“2<<3”。

最後,我們再來考慮一種情況,當要移位的位數大於被操作數對應數據類型所能表示的最大位數時,結果會是怎樣呢?比如,1<<35=?呢?

這裏就涉及到移位運算的另外一些規則:

byte、short、char在做移位運算之前,會被自動轉換爲int類型,然後再進行運算。
byte、short、int、char類型的數據經過移位運算後結果都爲int型。
long經過移位運算後結果爲long型。
在左移位(<<)運算時,如果要移位的位數大於被操作數對應數據類型所能表示的最大位數,那麼先將要求移位數對該類型所能表示的最大位數求餘後,再將被操作數移位所得餘數對應的數值,效果不變。比如1<<35=1<<(35%32)=1<<3=8。
對於有符號右移位(>>)運算和無符號右移位(>>>)運算,當要移位的位數大於被操作數對應數據類型所能表示的最大位數時,那麼先將要求移位數對該類型所能表示的最大位數求餘後,再將被操作數移位所得餘數對應的數值,效果不變。。比如100>>35=100>>(35%32)=100>>3=12。

下面的測試代碼驗證了以上的規律:

public abstract class Test {   
        public static void main(String[] args) {
            System.out.println("1 << 3 = " + (1 << 3));
            System.out.println("(byte) 1 << 35 = " + ((byte) 1 << (32 + 3)));
            System.out.println("(short) 1 << 35 = " + ((short) 1 << (32 + 3)));
            System.out.println("(char) 1 << 35 = " + ((char) 1 << (32 + 3)));
            System.out.println("1 << 35 = " + (1 << (32 + 3)));
            System.out.println("1L << 67 = " + (1L << (64 + 3)));
            // 此處需要Java5.0及以上版本支持
            System.out.println("new Integer(1) << 3 = " + (new Integer(1) << 3));
            System.out.println("10000 >> 3 = " + (10000 >> 3));
            System.out.println("10000 >> 35 = " + (10000 >> (32 + 3)));
            System.out.println("10000L >>> 67 = " + (10000L >>> (64 + 3)));
        }  
    } 
public abstract class Test {   
        public static void main(String[] args) {
            System.out.println("1 << 3 = " + (1 << 3));
            System.out.println("(byte) 1 << 35 = " + ((byte) 1 << (32 + 3)));
            System.out.println("(short) 1 << 35 = " + ((short) 1 << (32 + 3)));
            System.out.println("(char) 1 << 35 = " + ((char) 1 << (32 + 3)));
            System.out.println("1 << 35 = " + (1 << (32 + 3)));
            System.out.println("1L << 67 = " + (1L << (64 + 3)));
            // 此處需要Java5.0及以上版本支持
            System.out.println("new Integer(1) << 3 = " + (new Integer(1) << 3));
            System.out.println("10000 >> 3 = " + (10000 >> 3));
            System.out.println("10000 >> 35 = " + (10000 >> (32 + 3)));
            System.out.println("10000L >>> 67 = " + (10000L >>> (64 + 3)));
        }  
    } 

運行結果:

1 << 3 = 8
(byte) 1 << 35 = 8
(short) 1 << 35 = 8
(char) 1 << 35 = 8
1 << 35 = 8
1L << 67 = 8
new Integer(1) << 3 = 8
10000 >> 3 = 1250
10000 >> 35 = 1250
10000L >>> 67 = 1250

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章