1. 程式人生 > >JAVA移位運算與進位制轉換

JAVA移位運算與進位制轉換

關於資料的基礎知識

眾所周知,在計算機中,資料都是以2進位制的方式儲存。舉個最最最簡單的例子

10進位制整數 1 以2進製表示 1
10進位制整數 2 以2進製表示為10
10進位制整數 16 以2進製表示為10000

這樣看起來很簡單的樣子。但是在我們解讀2進位制資料的時候會有個很大的麻煩。例如:

A把10進位制的整數16儲存成10000傳送給B。如果A不告訴B他使用的是10進位制資料的話,那麼多於B來說,10000可以為10進位制的16,也可以是8進位制的20,也可以是16進位制的10,B就會懵逼的問A:“你傳給我的是什麼鬼“。

上面的例子足以說明資料型別對於資料的重要性。

為什麼計算機是2進位制呢?因為理論是最佳的進位制是e進位制,即2.718…。正常來說3進位制與其更為接近,然而對於製造業來說,2進位制更方便生產,所以就變成了2進位制。

在java中1個int型別佔用多少位呢?我們來做個測試

System.out.println(-1);
// output:11111111111111111111111111111111

A仔細的數了幾遍以後確定確實是32位。於是A說:”新技能get!原來int是32位”。
然而A又錯了。在java中,int確實固定為32位。不過在其他語言中int並不一定是32位(在C中有仔細說明int型別的問題)。

Java中short是16位-32768~32768這時:

System.out.println((short)32770);
//output: -32766

有符號型別的最高位表示正負(0正,1負),所以對於16位有符號型別的數字最大值應該為Math.pow(2,15)。當數字在Math.pow(2,15)Math.pow(2,16)之間的時候,就會出現最高位被置為1,導致出現負數。(java沒有無符號型別)

說了這麼多進入正題:
    在JAVA中總共有3種移位運算子。
* >> 有符號右移
* << 右符號左移
* >>> 無符號右移

有符號和無符號的區別在於對最高位的處理。

有符號移位的時候回保留最高位的狀態,例:

System.out.println(Integer.toBinaryString(-1));
//output: 11111111111111111111111111111111

System.out.println(Integer.toBinaryString(-1>>1));
//output: 11111111111111111111111111111111

System.out.println(Integer.toBinaryString(-1>>>1));
//output: 01111111111111111111111111111111

因為對於低位的處理無符號和有符號是相同的所以不存在無符號左移

那麼這跟進位制轉換有毛關係?

再舉個栗子:

2進位制的10000等於16進位制的多少呢?

思路1:10000是2的4次方等於16等於16進位制的10。(確實毛關係沒有!)

不過這是人的思維。

思路2:運用移位運算進行計算:

在2進制中4位代表16進位制一位所以10000等於00010000把4位看作1位那麼0001等於10000等於0所以16進製為10.

再來看:
在2進制中3位代表8進制中的1位,10000等於010000等於八進位制中的2位010000,其中010等於2所以8進制中為20。

A恍然大悟!原來是這麼玩的,這時A又問了十分愚蠢的問題:平時都是用10進位制,研究這個有啥用?

然而位運算就是2進位制的,雖然程式碼上寫的是9 & 2其實這個也是通過對應2進位制進行計算的。

A最後問:說了著麼多你到底想告訴我什麼?

因為這就是Integer中對於進位制轉換的實現。


 /**
     * Convert the integer to an unsigned number.
     */
    private static String toUnsignedString0(int val, int shift) {
        // assert shift > 0 && shift <=5 : "Illegal shift value";
        int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
        int chars = Math.max(((mag + (shift - 1)) / shift), 1);
        char[] buf = new char[chars];

        formatUnsignedInt(val, shift, buf, 0, chars);

        // Use special constructor which takes over "buf".
        return new String(buf, true);
    }


     static int formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) {
        int charPos = len;
        int radix = 1 << shift;
        int mask = radix - 1;
        do {
            buf[offset + --charPos] = Integer.digits[val & mask];
            val >>>= shift;
        } while (val != 0 && charPos > 0);

        return charPos;
    }

A吐血身亡!!!!