轉載請註明原創出處,謝謝!
說在前面
之前在JVM菜鳥進階高手之路十(基礎知識開場白)的時候簡單提到了二進位制相關問題,最近在看RocketMQ的原始碼的時候,發現涉及二進位制的內容蠻多,jdk原始碼裡面也是有很多涉及到二進位制相關的操作,今天這篇文章僅僅是掃盲篇,後續會介紹靈活運用篇。
說明
任何東西都有規範,提到JAVA就會提到2個規範,JAVA語言規範、JVM規範。JAVA語言規範主要定義JAVA的語法、變數、型別、文法等等,JVM規範主要定義Class檔案型別、執行時資料、幀棧、虛擬機器的啟動、虛擬機器的指令集等等。
- JAVA語言規範主要定義什麼是JAVA語言。
- JVM規範主要定義JVM內部實現,二進位制class檔案和JVM指令集等。
規範中數字的內部表示和儲存
JAVA八種基本資料型別: 整形:byte,short,int,long 浮點型:float,double 布林型:boolean 字元型:char
資料型別 | 所佔位數 |
---|---|
int | 32bit |
short | 16bit |
long | 64bit |
byte | 8bit |
char | 16bit |
float | 32bit |
double | 64bit |
boolean | 1bit |
**備註:**1位元組=8位(1 byte = 8bit)
整數的表示
- 原始碼:第一位為符號位(0表示正數,1表示負數)。
- 反碼:符號位不動,原碼取反。
- 負數補碼:符號位不動,反碼加1。
- 正數補碼:和原始碼相同。
**備註:**補碼的好處:
- 使用補碼可以沒有任何歧義的表示0。
- 補碼可以很好的參與二進位制的運算,補碼相加符號位參與運算,這樣就簡單很多了。
浮點數表示
在上圖中,我們瞭解到Float與Double都是支援IEEE 754
我們以float來說明:
IEEE754單精度浮點格式共32位,包含三個構成欄位:23位小數f,8位偏置指數e,1位符號s。將這些欄位連續存放在一個32位字裡,並對其進行編碼。其中0:22位包含23位的小數f; 23:30位包含8位指數e;第31位包含符號s。
一個實數V在IEEE 754標準中可以用V=(-1)s×M×2E 的形式表示,說明如下:
- 符號s(sign)決定實數是正數(s=0)還是負數(s=1),對數值0的符號位特殊處理。
- 有效數字M(significand)是二進位制小數,M的取值範圍在1≤M<2或0≤M<1。
- 指數E(exponent)是2的冪,它的作用是對浮點數加權。
符號位 | 指數位 | 小數位 |
---|---|---|
1位 | 8位 | 23位 |
例如根據IEEE754,計算11000001000100000000000000000000的單精度浮點的值。
解題:
1 | 10000010 | 00100000000000000000000 |
---|---|---|
符號位 | 指數 | 尾數由於指數不是全部為0 所以小數位附加1 |
1 | 10000010 | **1.**00100000000000000000000 |
-1 | 2^(130-127) | (2^0 + 2^-3) |
結論: -1 * (2^0 + 2^-3) * 2^(130-127) =-9
同樣,你也可以驗證一下十進位制浮點數0.1的二進位制形式是否正確,你會發現,0.1不能表示為有限個二進位制位,因此在記憶體中的表示是舍入(rounding)以後的結果,即 0x3dcccccd, 十進位制為0.100000001, 誤差0.000000001由此產生了。
說到這裡JVM菜鳥進階高手之路十(基礎知識開場白)的有些問題其實都解答了,所以涉及到錢的小數型別必須使用BigDecimal,禁止使用float和double。
進位制的概念
我們常用的進位制有二進位制、八進位制、十進位制和十六進位制,十進位制是最主要的表達形式。
二進位制是0和1;八進位制是0-7;十進位制是0-9;十六進位制是0-9+A-F(大小寫均可)。
位運算子
按位與(&)
兩位全為1,結果才為1:
0&0=0;
0&1=0;
1&0=0;
1&1=1;
複製程式碼
用法:
- 清零:如果想要一個單位清零,那麼使其全部二進位制為0,只要與一個各位都為零的數值想與,結果為零。
- 取一個數中指定位:找一個數,對應X要取的位,該數的對應位為1,其餘位為零,此數與X進行“與運算”可以得到X中的指定位。
例如:設X=1010 1110,取X的低4位,用X & 0000 1111 = 0000 1110 就可以得到。
按位或(|)
只要有一個為1,結果就為1:
0|0=0;
0|1=1;
1|0=1;
1|1=1;
複製程式碼
**用法:**常用來對一個資料的某些位置1;找到一個數,對應X要置1的位,該數的對應位為1,其餘位為零。此數與X相或可使X中的某些位置1。
例如:將X=1010 0000 的低四位置1,用X | 0000 1111 =1010 1111 就可以得到。
異或運算(^)
**兩個相應位為“異”(值不同),則該位結果為1,否則為0: **
0^0=0;
0^1=1;
1^0=1;
1^1=0;
複製程式碼
用法:
- 使特定位翻轉:找一個數,對應X要翻轉的各位,該數的對應位為1,其餘位為零,此數與X對應位異或就可以得到; 例如:X=1010 1110,使X低4位翻轉,用X ^ 0000 1111 = 1010 0001就可以得到
- 與0相異或,保留原值 例如:X ^ 0000 0000 = 1010 1110
- 兩個變數交換值的方法: 1、藉助第三個變數來實現: C=A; A=B; B=C; 2、 利用加減法實現兩個變數的交換:A=A+B; B=A-B;A=A-B; 3、用位異或運算來實現:利用一個數異或本身等於0和異或運算子合交換律 例如:A = A ^ B; B = A ^ B; A = A ^ B;
取反運算(~)
對於一個二進位制數按位取反,即將0變1,1變0: ~1=0; ~0=1;
左移運算(<<)
- 將一個運算物件的各二進位制位全部左移若干位(左邊的二進位制丟棄,右邊補零) 2<<1 = 4 : 10 <<1 =100=4
- 若左移時捨棄的高位不包括1,則每左移一位,相當於該數乘以2。 -14(二進位制:1111 0010)<< 2= (1100 1000) (高位包括1,不符合規則)
右移運算(>>)
將一個數的各二進位制位全部右移若干位,正數左補0,負數左補1,右邊丟棄。運算元每右移一位,相當於該數除以2.
左補0 or 補1 得看被移數是正還是負。 例:4 >> 2 = 1 例:-14(1111 0010) >> 2 = -4 (1111 1100 )
無符號右移運算(>>>)
各個位向右移指定的位數。右移後左邊突出的位用零來填充。移出右邊的位被丟棄
各個位向右移指定的位數。右移後左邊突出的位用零來填充。移出右邊的位被丟棄
例如: -14>>>2
即-14(1111 1111 1111 1111 1111 1111 1111 0010)>>> 2
=(0011 1111 1111 1111 1111 1111 1111 1100)
= 1073741820
Java列印整數的二進位制表示
int a = 1120429670;
for (int i = 0; i < 32; i++) {
int t = (a & 0x80000000 >>> i) >>> (31 - i);
System.out.print(t);
}
複製程式碼
說明:
- 0x80000000是數的十六進位制表示,轉成二進位制表示為10000000000000000000000000000000
- 運算的優先順序,移位運算高於邏輯運算,>>>高於&
- 位邏輯與運算 1&1 = 1 ,0&1 = 0
>>>
無符號右移,移出部分捨棄,左邊位補0;
歡迎積極留言討論關於你在實際運用中瞭解到二進位制的一些優秀實踐,期待你的留言!!!
如果讀完覺得有收穫的話,歡迎點贊、關注、加公眾號【匠心零度】。
個人公眾號,歡迎關注,查閱更多精彩歷史!!!