Java編程的邏輯 (4) - 整數的二進制表示與位運算

本系列文章經補充和完善,已修訂整理成書《Java編程的邏輯》,由機械工業出版社華章分社出版,於2018年1月上市熱銷,讀者好評如潮!各大網店和書店有售,歡迎購買,京東自營連接http://item.jd.com/12299018.htmlhtml


上節咱們提到正整數相乘的結果竟然出現了負數,要理解這個行爲,咱們須要看下整數在計算機內部的二進制表示。編程

十進制swift

要理解整數的二進制,咱們先來看下熟悉的十進制。十進制是如此的熟悉,咱們可能已忽略了它的含義。好比123,咱們不假思索就知道它的值是多少。微信

但其實123表示的1*(10^2) + 2*(10^1) + 3*(10^0),(10^2表示10的二次方),它表示的是各個位置數字含義之和,每一個位置的數字含義與位置有關,從右向左,第一位乘以10的0次方, 即1,第二位乘以10的1次方,即10,第三位乘以10的2次方,即100,依次類推。post

換句話說,每一個位置都有一個位權,從右到左,第一位爲1,而後依次乘以10,即第二位爲10,第三位爲100,依次類推。spa

正整數的二進制表示code

正整數的二進制表示與此相似, 只是在十進制中,每一個位置能夠有10個數字,從0到9,但在二進制中,每一個位置只能是0或1。位權的概念是相似的,從右到左,第一位爲1,而後依次乘以2,即第二位爲2,第三位爲4,依次類推。htm

看一些數字的例子吧:blog

二進制 十進制
10 2
11 3
111 7
1010 10

負整數的二進制表示get

十進制的負數表示就是在前面加一個負數符號-,例如-123。但二進制如何表示負數呢?

其實概念是相似的,二進制使用最高位表示符號位,用1表示負數,用0表示正數。

但哪一個是最高位呢?整數有四種類型,byte/short/int/long,分別佔1/2/4/8個字節,即分別佔8/16/32/64位,每種類型的符號位都是其最左邊的一位。

爲方便舉例,下面假定類型是byte,即從右到左的第8位表示符號位。

但負數表示不是簡單的將最高位變爲1,好比說:

  • byte a = -1,若是隻是將最高位變爲1,二進制應該是10000001,但實際上,它應該是11111111。
  • byte a=-127,若是隻是將最高位變爲1,二進制應該是11111111,但實際上,它卻應該是10000001。 

和咱們的直覺正好相反,這是什麼表示法?這種表示法稱爲補碼錶示法,而符合咱們直覺的表示稱爲原碼錶示法,補碼錶示就是在原碼錶示的基礎上取反而後加1。取反就是將0變爲1,1變爲0。

負數的二進制表示就是對應的正數的補碼錶示,好比說:

  • -1:1的原碼錶示是00000001,取反是11111110,而後再加1,就是11111111。
  • -2:2的原碼錶示是00000010,取反是11111101,而後再加1,就是11111110。
  • -127:127的原碼錶示是01111111,取反是10000000,而後再加1,就是10000001。 

給定一個負數二進制表示,要想知道它的十進制值,能夠採用相同的補碼運算。好比:10010010,首先取反,變爲01101101,而後加1,結果爲01101110,它的十進制值爲110,因此原值就是-110。直覺上,應該是先減1,而後再取反,但計算機只能作加法,而補碼的一個良好特性就是,對負數的補碼錶示作補碼運算就能夠獲得其對應整數的原碼,正如十進制運算中負負得正同樣。

byte類型,正數最大表示是01111111,即127,負數最小表示(絕對值最大)是10000000,即-128,表示範圍就是 -128到127。其餘類型的整數也相似,負數能多表示一個數。

負整數爲何採用補碼呢?

負整數爲何要採用這種奇怪的表示形式呢?緣由是:只有這種形式,計算機才能實現正確的加減法。

計算機其實只能作加法,1-1實際上是1+(-1)。若是用原碼錶示,計算結果是不對的。好比說:

1  -> 00000001
-1 -> 10000001
+ ------------------
-2 -> 10000010

用符合直覺的原碼錶示,1-1的結果是-2。

若是是補碼錶示:

1  -> 00000001
-1 -> 11111111
+ ------------------
0  ->  00000000 

結果是正確的。

再好比,5-3:

5  -> 00000101
-3 -> 11111101
+ ------------------
2  ->  00000010 

結果也是正確的。

就是這樣的,看上去可能比較奇怪和難以理解,但這種表示實際上是很是嚴謹和正確的,是否是很奇妙?

理解了二進制加減法,咱們就能理解爲何正數的運算結果可能出現負數了。當計算結果超出表示範圍的時候,最高位每每是1,而後就會被看作負數。好比說,127+1:

127   -> 01111111
1     -> 00000001
+ ------------------
-128  -> 10000000 

計算結果超出了byte的表示範圍,會被看作-128。

十六進制

二進制寫起來太長,爲了簡化寫法,能夠將四個二進制位簡化爲一個0到15的數,10到15用字符A到F表示,這種表示方法稱爲16進制,以下所示:

2進制 10進制 16進制
1010 10 A
1011 11 B
1100 12 C
1101 13 D
1110 14 E
1111 15 F

能夠用16進制直接寫常量數字,在數字前面加0x便可。好比10進制的123,用16進製表示是0x7B,即123 = 7*16+11。給整數賦值或者進行運算的時候,均可以直接使用16進制,好比:

int a = 0x7B;

Java中不支持直接寫二進制常量,好比,想寫二進制形式的11001,Java中不能直接寫,能夠在前面補0,補足8位,爲00011001,而後用16進製表示,即 0x19。

查看整數的二進制和十六進制表示

在Java中,能夠方便的使用Integer和Long的方法查看整數的二進制和十六進制表示,例如:

int a = 25;
System.out.println(Integer.toBinaryString(a)); //二進制
System.out.println(Integer.toHexString(a));  //十六進制
System.out.println(Long.toBinaryString(a)); //二進制
System.out.println(Long.toHexString(a));  //十六進制

位運算

位運算是將數據看作二進制,進行位級別的操做,Java不能單獨表示一個位,可是能夠用byte表示8位,能夠用16進制寫二進制常量。好比: 0010表示成16進制是 0x2, 110110表示成16進制是 0x36。

位運算有移位運算和邏輯運算。

移位有:

  • 左移:操做符爲<<,向左移動,右邊的低位補0,高位的就捨棄掉了,將二進制看作整數,左移1位就至關於乘以2。
  • 無符號右移:操做符爲>>>,向右移動,右邊的捨棄掉,左邊補0。
  • 有符號右移:操做符爲>>,向右移動,右邊的捨棄掉,左邊補什麼取決於原來最高位是什麼,原來是1就補1,原來是0就補0,將二進制看作整數,右移1位至關於除以2。

例如:

int a = 4; // 100
a = a >> 2; // 001,等於1
a = a << 3 // 1000,變爲8

邏輯運算有:

  • 按位與 &:兩位都爲1才爲1
  • 按位或 |:只要有一位爲1,就爲1
  • 按位取反 ~: 1變爲0,0變爲1
  • 按位異或 ^ :相異爲真,相同爲假

大部分都比較簡單,就不詳細說了。具體形式,例如:

int a = ...; 
a = a & 0x1 // 返回0或1,就是a最右邊一位的值。
a = a | 0x1 //無論a原來最右邊一位是什麼,都將設爲1

小結

本節咱們討論了整數的二進制表示,須要注意的就是負數的二進制表示,以及計算機進行二進制加減操做的過程,從而咱們就能理解爲何有的時候正整數計算會出現負數。

咱們一樣討論了整數的位運算,須要注意的就是無符號右移和有符號右移的區別。

理解了整數,那小數呢?

---------------- 

未完待續,查看最新文章,敬請關注微信公衆號「老馬說編程」(掃描下方二維碼),深刻淺出,老馬和你一塊兒探索Java編程及計算機技術的本質。原創文章,保留全部版權。

-----------

更多相關原創文章

計算機程序的思惟邏輯 (1) - 數據和變量

計算機程序的思惟邏輯 (2) - 賦值

計算機程序的思惟邏輯 (3) - 基本運算

計算機程序的思惟邏輯 (5) - 小數計算爲何會出錯?

計算機程序的思惟邏輯 (6) - 如何從亂碼中恢復 (上)?

計算機程序的思惟邏輯 (7) - 如何從亂碼中恢復 (下)?

計算機程序的思惟邏輯 (8) - char的真正含義

相關文章
相關標籤/搜索