原創文章,如有轉載,請註明出處:http://blog.csdn.net/yihui823/article/details/6754038
其實和java是一樣的,java也通用。
我們人對數位理解,是從十進位開始的。從小受到的教育,也是從 1+9 = 10開始的。但是,對於目前的電腦來說,它們的世界是二進位的世界。電腦用一個高電平表示1,用一個低電平表示0。
如果我們要表示十進位數10,對於電腦來說,必須轉換成位元1010。當然,電腦不但用“0”和“1”來表示資料,還用“0”和“1”來表示字母,表示操作,表示運算,表示推理,表示一切的一切。對於電腦所有操作的原操作,都是“0”和“1”的操作。
對於這些電腦的原操作,也是和我們的認識不同的。我們學的基礎運算,例如1+1=2,那是一個加法而已,是不能再切分的了。但是電腦的原操作,只有“與”、“或”、“異或”、“非”以及位移等操作。加法是需要用這些原操作組合起來才能實現的。
說的比較籠統。這個也不是一兩句才能說清楚的。在現在的進階語言中,加法和位元運算的操作符可以同時使用,所以我們也不需要太過深究位元運算怎麼實現加法,只要知道有位元運算這麼回事就行了。
以下是基礎的位元運算
0 & 0 = 0; 0& 1 = 0; 1 & 0 = 0; 1 & 1 = 1
0 | 0 = 0; 0 | 1= 1; 1 | 0 = 1; 1 | 1 = 1
0 ^ 0 = 0;1 ^ 0= 1;0 ^ 1 = 1; 1 ^ 1 = 0
另外,位元組的概念需要說明一下。位元組(Byte)是電腦資訊技術用於計量儲存容量和傳輸容量的一種計量單位,1個位元組等於8位二進位。Java裡的byte,就是位元組。
對於不帶正負號的整數來說,一個位元組能表示的最大的數,就是255了,也就是“11111111”。但是對於一個有符號的整數,因為需要把左邊第一位用來表示正負,所以能表示的最大的數,就是127了。也就是“0111111”。能表示的最小的數也可以如此推算。
在java裡面,一個int就是用4個位元組來表示的。因為java沒有不帶正負號的整數的類型,所以第一位一定是符號位。
如果我們把byte強制轉換成int類型,int的最低的8位與byte相同,之前的3個位元組,都用符號位來填補。也就是說,如果是一個負數byte,那麼轉化成int之後,前3個位元組都是1。
我們可以用下面這段代碼來看:
byte b1 = 10;System.out.println("b1=" + Byte.toString(b1));System.out.println("b1轉成int後的二進位:" + Integer.toBinaryString(b1));byte b2 = -10;System.out.println("b2=" + Byte.toString(b2));System.out.println("b2轉成int後的二進位:" + Integer.toBinaryString(b2));
結果顯示:
b1=10
b1轉成int後的二進位:1010
b2=-10
b2轉成int後的二進位:11111111111111111111111111110110
b1是正數,轉成int型後,前面補0,所以還是00001010,只是0000在轉String的時候去掉了。
b2是負數,轉成int型後,前面補1,所以就是11111111111111111111111111110110了。
細心的朋友一定發現了不對勁的地方。是的,-10的二進位,最後幾位是11110110,不是我們想象中的10001010。這裡有個概念,叫補碼。
為什麼要有這麼一個概念。還得從符號位來說。如果只是符號位來表示正負,後面的資料都一樣,那麼就會有個問題,+0和-0的事情。正數我們可以表示的從0~127,負數我們可以表示-0 ~ -127,那麼一共只能表示255個數字,因為0和-0是一個數字。為瞭解決這個問題,所以有了補碼的概念。也就是說,如果是負數,除了符號位用1表示外,剩下的位元都反一下,然後-1.
那麼,-10的二進位的推算步驟,應該是:
1,10的二進位,去掉符號位是0001010
2,減一,就是0001001
3,反過來,就是1110110
4,加上符號位,就是11110110
這種情況下,就沒有-0的概念了。10000000表示的數,應該是:
1, 去掉符號位,資料位元是0000000
2, 因為符號位是1,所以需要補碼計算
3, 反過來,就是1111111
4, 加1,就是10000000。
所以,10000000表示的十進位數是-128
這樣,一個byte可以表示 -128 ~127,正好256個數字,一個都不浪費了。
如果我們只要顯示byte的位元,那怎麼辦?那我們就先把byte轉成int,然後把前面的3個byte都去掉,只留下最後一個byte的資料就行了。代碼如下:
byte b1 = 10;System.out.println("b1=" + Byte.toString(b1));System.out.println("b1的二進位:" + Integer.toBinaryString(0xff & b1));byte b2 = -10;System.out.println("b2=" + Byte.toString(b2));System.out.println("b2的二進位:" + Integer.toBinaryString(0xff & b2));
主要是因為我們找不到Byte類的toBinaryString函數,所以只好借Integer的用用啦。
這裡還需要說明一下,位移操作。如果把一個byte左移一位,那麼最右邊將補一個0,最左邊的資料就丟掉了。而右移則不同,最左邊補的是符號位,也就是最高的那個位。如果是負數,右移後還是負數。
看這段代碼:
byte b1 = 10;System.out.println("位移前b1=" + Byte.toString(b1));System.out.println("位移前b1的二進位:" + Integer.toBinaryString(0xff & b1));b1 = (byte)(b1 >> 2);System.out.println("位移後b1=" + Byte.toString(b1));System.out.println("位移後b1的二進位:" + Integer.toBinaryString(0xff & b1));byte b2 = -10;System.out.println("位移前b2=" + Byte.toString(b2));System.out.println("位移前b2的二進位:" + Integer.toBinaryString(0xff & b2));b2 = (byte)(b2 >> 2);System.out.println("位移後b2=" + Byte.toString(b2));System.out.println("位移後b2的二進位:" + Integer.toBinaryString(0xff & b2));
結果就是:
位移前b1=10
位移前b1的二進位:1010
位移後b1=2
位移後b1的二進位:10
位移前b2=-10
位移前b2的二進位:11110110
位移後b2=-3
位移後b2的二進位:11111101
一般在什麼情況下會用到位操作呢?我的一些總結:
1, 和裝置打交道的時候
基本上和裝置的介面,都是傳遞byte數組。byte數組如果需要轉成字串,自然不需要位元運算,只需要根據碼制進行解碼即可。但是如果轉成int/long等數值,則需要用位元運算來轉換。
2, 快速運算的時候
需要×2的時候,直接左移1位,操作很快,而且省CPU。
3, 節省記憶體的時候
直接用位元運算去判斷某個位元組,比轉成String判斷更省事。
還有什麼,我倒是一下子想不起來了。後面我會展示幾段代碼以表示位元運算的好處。