三分鐘熟悉進位轉換與位運算

2021-02-25 NO編程
進位和位運算簡介

進位也叫進位制,是一種記數方法,也稱進位計數法,利用這種記數法可以使用有限的數字符號來表示所有的數值。

一種進位中可以使用的數字符號的數目稱為這種進位制的基數,若一個進位的基數為 N,則可稱之為 N 進位,即表示數值時滿 N 進一。

在生活中最常用的是十進位,使用 10 個阿拉伯數字 0 到 9 進行記數。而在電子計算機領域,內部使用的是二進位,電路的狀態通過 0 和 1 表示來實現記數。八進位和十六進位計算機領域也較為常用,尤其十六進位。

位運算則是在程序中對二進位數的一元和二元運算操作。

在 JDK 以及框架源碼中都存在進位轉換和位運算的身影,作為開發人員應該熟悉基本的進位轉換和位運算(最起碼得能看懂吧)。

進位轉換

例如,十進數的 13,二進位的 1101,他們表示相同的數值,只是不同的表現形式而已,那麼不同進位之間如何相互轉換呢?

十進位轉換 N 進位,可以通過「短除法」求餘數然後倒序得到轉換結果,一個十進位數轉換為 N 進位就除以 N,例如:

將十進位數 123 轉換為二進位。

十進位 123 轉二進位

結果:1111011

將十進位數 123 轉換為十六進位。

十進位 123 轉十六進位

結果:7b

N 進位轉為十進位可以通過「按位權展開法」來轉換,即在 N 進位中,每個位置的數字乘以進位的基數為底的所處位置序號(從 0 開始)為指數的整數次冪,然後相加。例如:

將二進位數 1111011 轉換為十進位。

1 * 2^6 + 1 * 2^5 + 1 * 2^4 + 1 * 2^3 + 0 * 2^2 + 1 * 2^1 + 1 * 2^0
= 1 * 64 + 1 * 32 + 1 * 16 + 1 * 8 + 0 * 4 + 1 * 2 + 1 * 1
= 123

將十六進位數 7b 轉換為十進位

7 * 16^1 + b * 16^0
= 7 * 16 + 11 * 1
= 123

二進位與十六進位互轉

四位二進位數表示的範圍是 0 - 15,用四位二進位數可以表示十六進位的一位數,反之亦然。即二進位轉十六進位可以通過四(位)合一(位),十六進位轉二進位可以通過一(位)拆四(位)來轉換。例如:
二進位 1111011 轉換為十六進位,0111(7) - 1011(b) 即轉換結果為7b。
十六進位 7b 轉為二進位,7(0111) - b(十進位 11,二進位 1011) 即轉換結果為 1111011。

二進位與八進位互轉

三位二進位數表示的範圍是 0 - 7,用三位二進位數可以表示八進位的一位數,反之亦然。即二進位轉八進位可以通過三(位)合一(位),八進位轉二進位可以通過一(位)拆三(位)來轉換。例如:
二進位 1111011 轉換為八進位,001(1) - 111(7) - 011(3) 即轉換結果為173。
八進位 173 轉為二進位,1(001) - 7(111) - 3(011) 即轉換結果為 1111011。

負數的二進位

前面介紹的都是正數的二進位轉換,那麼負數的二進位如何表示和轉換的?

首先,我們先了解一下原碼、反碼、補碼,一個數在計算機中的二進位表示形式叫做這個數的機器數,機器數的最高位稱為符號位,正數符號位為 0,負數符號位為 1,而符號位之外部分稱為機器數的真值(表示真正的數值部分),原碼、反碼、補碼是機器數的表示方法。

原碼: 原碼的表示方法,最高位是符號位,其餘部分表示數值(真值)。正數符號位為 0,負數符號位為 1,0 的符號位可以為 0 或 1(+0 和 -0)。

例如,我們用 8 位二進位表示一個數,則 +11 的原碼為 00001011,-11 的原碼就是 10001011。

在數學中 1 + (-1) = 0,如果使用原碼直接參與數學運算,00000001 + 10000001 = 10000010(換算成十進位為 -2),顯然不對。所以原碼的符號位不能直接參與運算,必須和其他位分開,這就增加了硬體的開銷和複雜性。為了便於 ALU (算術邏輯單元,實現多組算術運算和邏輯運算的組合邏輯電路)的設計,又發展出反碼、補碼。

反碼: 反碼的表示方法,正數的反碼等於其原碼,而負數的反碼通過保留其符號位,將其原碼的數值位(真值)取反。

例如,同樣用 8 位二進位表示一個數,11 = 00001011(原碼) = 00001011(反碼),
-11 = 10001011(原碼) = 11110100(反碼)。

雖然,反碼可以消除原碼存在的計算問題,由於反碼存在多餘的負零等問題,此方式並未被廣泛應用。

補碼: 補碼的表示方式,正數和 0 的補碼等於其原碼,且補碼的 0 只有一個表示方式,不分 +0 和 -0。負數是將他的反碼加 1 得到補碼。

例如,11 = 00001011(原碼) = 00001011(反碼) = 00001011(補碼),-11 = 10001011(原碼) = 11110100(反碼) = 11110101(補碼)。

一種簡單方式算出補碼

負數的補碼一般情況下通過負數的原碼得到反碼,在將反碼加 1 獲得補碼。這裡有一種簡單的計算補碼方法。

將對應的正數原碼從最低比特位向高比特位查找。

若該比特位為 0,補碼對應比特位填 0,繼續向高比特位查找。

若找到第一個為 1 的比特位,將補碼對應比特位填 1。

然後,將其餘未轉換的比特位全部取反。

例如:計算 -20 的補碼。

計算 -20 的補碼計算機為什麼使用補碼

為了簡化 ALU 設計,減法轉換為加法來計算,例如,1 - 1 可以轉換為 1 + (-1) = 00000001 + 11111111 = 100000000 (由於加數和被加數都是 8 位,因此運算結果也限制在 8 位,前面溢出的比特位 1 忽略,所以結果為 00000000 = 0),即一個數加上另一個數的補碼來表示。

這樣只要有加法電路即可完成各種有號數加減法,對於乘除法,乘法在計算機中其實就是不斷的做加法,除法就是相減,本質也是加法,所以四則運算的基礎都是由加法而來,電路設計得到了很大簡化。補碼解決了原碼和反碼出現的問題,因此計算機中數值使用補碼方式來計算和存儲的。

Java 內置的進位轉換 API

JDK 的 Integer 和 Long 類提供了常用的進位相互轉換方法。

進位轉換java.lang.Integerjava.lang.Long10進位 → 2進位toBinaryString(int i)toBinaryString(long i)10進位 → 8進位toOctalString(int i)toOctalString(long i)10進位 → 16進位toHexString(int i)toHexString(long i)10進位 → n 進位toString(int i, int radix)toString(long i, int radix)n 進位 → 10進位valueOf(String s, int radix)
parseInt(String s, int radix)valueOf(String s, int radix)
parseLong(String s, int radix)

System.out.println(Integer.toBinaryString(13)); // 十轉二,結果為 1101
System.out.println(Integer.toOctalString(13));  // 十轉八,結果為 15
System.out.println(Integer.toHexString(13));    // 十轉十六,結果為 d
System.out.println(Integer.toString(13, 5));    // 十轉五,結果為 23
System.out.println(Integer.valueOf("23", 5));   // 五轉十,結果為 13

位數補齊

如 int 類型數據長度是 32 位,輸出時前面的「0」會被省略,想補齊可以使用 commons-lang3-*.jar 的 StringUtils 工具類對輸出的字符串位數補齊。示例:

String fullStr = StringUtils.leftPad("1010", 32, "0"); // 對二進位 1010 位數補齊
System.out.println(fullStr); // 輸出結果:00000000000000000000000000001010

Java 中進位前綴

Java 對二進位、八進位、十六進位提供了字面量前綴的表現形式,可以直接使用這幾種進位形式計算或賦值,例如:

// 都表示十進位的 10
int i1 = 10;      // 十進位,沒有前綴
int i2 = 0b1010; // 二進位,前綴   0b(Java 7 或更高版本)
int i3 = 012;      // 八進位,前綴   0
int i4 = 0xA;      // 十六進位,前綴 0x

System.out.println(i2 + 1);
System.out.println(i3 + 1);
System.out.println(i4 + 1);

位運算

位運算是在程序中對二進位數的一元和二元運算操作,其運算符有 & ,| ,~ ,^ ,<<, >>, >>> ,接下來我們來逐個說明:

& (按位與)

按位「與」操作處理兩個長度相同的二進位數,兩個相應的二進位都為 1,該位的結果值才為 1,否則為 0。
如圖:10 & 3 (int 類型長度 32 位,下圖補位的 0 已被省略)。

10 & 3| (按位或)

按位「或」操作處理兩個長度相同的二進位數,兩個相應的二進位只要有一個為 1,該位的結果值就為 1。
例如:10 | 3 (int 類型長度 32 位,下圖補位的 0 已被省略)。

10 | 3~ (按位非)

該操作符是一元運算符,對一個二進位數的每一位執行邏輯反操作,使相應的位 1 變為 0,0變為 1。
例如:~ 3

~ 3^ (按位異或)

按位「異或」操作處理兩個等長的二進位數,如果某對應位值不同則結果值為 1,否則為 0。
例如:10 ^ 3 (int 類型長度 32 位,下圖補位的 0 已被省略)。

10 ^ 3<< (左移)

向左進行移位操作,高位丟棄,移位後空缺的低位補 0。
例如:10 << 2

10 << 2>> (右移)

向右移位,移位後空缺高位補 0,若為負數,高補 1。
例如:10 >> 2 和 -10 >> 2

10 >> 2

-10 >> 2>>> (無符號右移)

向右移位,無論正負,高位空缺部分補 0。
例如:10 >>> 2 和 -10 >>> 2

10 >>> 2

-10 >>> 2應用示例

m * 2^n 或 m / 2^n

System.out.println(2 << 1); // 2 * 2^1 = 4 ,相當於乘以 2^1
System.out.println(3 >> 1); // 3 / 2^1 = 1 , 相當於除以 2^1 向下取整

奇數偶數判斷

if(m & 1 == 1) {
    System.out.println("奇數");
} else {
    System.out.println("偶數");
}

隨機概率

// lr = 隨機數;
if ((lr & 0x3) == 0) {
    // 有 1/4 的概率執行該代碼塊
}

- END -

參考:
維基百科 - https://zh.wikipedia.org

相關焦點

  • [洛穀日報第79期]二進位與位運算
    接下來介紹一下二進位數對於二進位數,首先要知道二進位是什麼。類似於十進位,二進位也是一種進位(廢話),但二進位運算遵循的規則是「進二」而不是我們熟悉的「進十」。其他進位也可以用類似的方法進行轉換,例題P1143 進位轉換(https://www.luogu.org/problemnew/show/P1143)原碼、反碼和補碼原碼,指一個二進位數左邊加上符號位後所得到的碼,且當二進位數大於0時,符號位為0;二進位數小於0時,符號位為1;二進位數等於0時,符號位可以為0或1。
  • EV3關於10進位轉換2進位的詳解
    哈咯各位大家好之前突發奇想想做一個關於十進位轉化成二進位的想法,在這裡給大家講解一下。關於數字有多進位的,十進位、二進位、三進位、八進位、十六進位....等等。對我們最熟悉的莫過於十進位和二進位了,接下來給大家講解下如何實現將十進位轉化為二進位。
  • 10、進位轉換:二進位、八進位、十六進位、十進位之間的轉換
    將二進位、八進位、十六進位轉換為十進位二進位、八進位和十六進位向十進位轉換都非常容易,就是「按權相加」。所謂「權」,也即「位權」。假設當前數字是 N 進位,那麼:更加通俗的理解是,假設一個多位數(由多個數字組成的數)某位上的數字是 1,那麼它所表示的數值大小就是該位的位權。
  • 二進位、八進位、十進位和十六進位數之間的轉換方法
    3、二、八、十進位數之間轉換(1)二進位 數與八進位數之間的轉換轉換方法①把二進位數轉換為八進位數時,按「三位並一位」的方法進行。以小數點為界,將整數部分從右向左每三位一組,最高位不足三位時,添0補足三位;小數部分從左向右,每三位一組,最低有效位不足三位時,添0補足三位。然後,將各組的三位二進位數按權展開後相加,得到一位八進位數。②將八進位數轉換成二進數時,採用「一位拆三位」的方法進行。即 把八進位數每位上的數用相應的三位二進位數表示。
  • Java二進位和位運算,這一萬字準能餵飽你
    正文提及位運算,對絕大多數Java程式設計師來說,是一種既熟悉又陌生的感覺。熟悉是因為你在學JavaSE時肯定學過,並且在看一些開源框架(特別是JDK源碼)時都能看到它的身影;陌生是因為大概率我們不會去使用它。當然,不能「流行」起來是有原因的:不好理解,不符合人類的思維,閱讀性差…...
  • C# 16進位轉換10進位相關函數詳解
    C# 16進位轉換10進位相關函數詳解 在C#中可以對整型運算對象按位進行邏輯運算,同時也可以實現C# 16進位轉換10進位,C#10進位轉換2進位等功能。
  • 十進位數的編碼與運算
    這種編碼的優點是這4位基2碼之間滿足二進位的規則,而十進位數位之間則是十進位規則,故稱這種編碼為以二進位編碼的十進位(Binary Coded Decimal)數,簡稱BCD碼或二-十進位碼。另一個優點是在數字符的ASCII碼與這種編碼之間的轉換方便,即取每個數字符的ASCII碼的低4位的值便直接得到該數字的BCD碼,入/出操作簡便。
  • 計算中二進位與十進位之間的轉換,你能看懂嗎?
    眾所周知,計算機的運算中,都是用二進位來運算的,前兩天推送了兩篇關於計算機基礎知識的測試題,發現有很多童鞋對這個進位轉換還不是很熟悉。
  • 二進位、八進位、十進位、十六進位數的轉換方法
    一 、數制 計算機中採用的是二進位,因為二進位具有運算簡單,易實現且可靠,為邏輯設計提供了有利的途徑、節省設備等優點,為了便於描述,又常用八、十六進位作為二進位的縮寫。1 ----最左位∴ (30)10 =(1E)163、將P進位數轉換為十進位數把一個二進位轉換成十進位採用方法:把這個二進位的最後一位乘上20,倒數第二位乘上21,……,一直到最高位乘上2n,然後將各項乘積相加的結果就它的十進位表達式。
  • 如何進行進位轉換
    進位轉換由一組數碼符號和兩個基本因素「基數」與「位權」構成。   基數是指,進位計數制中所採用的數碼(數制中用來表示「量」的符號)的個數。   位權是指,進位制中每一固定位置對應的單位值。   2取餘法,即十進位數除2,餘數為權位上的數,得到的商值繼續除2,依此步驟繼續向下運算直到商為0為止。
  • 進位轉換
    除十進位外,我們常用的進位還有十六進位,八進位,當然最重要的就是二進位。十進位用0~9表示數據;十六進位除0~9外,還用A、B、C、D、E和F來表示10~15;八進位則是0~7;二進位只有0和1。任何進位數都可以簡單地轉換成十進位數,只需將展開式中的基數改為該進位基數。
  • 二進位,八進位,十進位,十六進位之間的轉換
    說起二進位,十進位等大家都很熟悉,但是如果讓你相互轉換,你還會麼?
  • 關於二進位、十進位、八進位、十六進位數據轉換計算方法詳細總結
    下面在安靜的音樂中,讓我們開始學習吧在我們接觸編程知識時,總會接觸有關進位轉換的知識,最常見的就是10進位與二進位或十六進位之間的轉換,很多時候我們總會遺忘,雖然現在也出現了很多可以直接使用的網絡在線的進位轉換工具,但考試中,我們就要靠自己通過公式進行運算了。
  • 零基礎學編程之進位轉換基礎
    01十進位十進位(逢10進1)是我們最常用的一種數位進位方式。十進位和二進位可以相互轉換,如下所示:至此,得出了結論:在封閉的四位運算中(超出4位就丟棄),這種設計和規定是非常合理的。可是,我們又有了疑問:(-1)*(1)=?,會不會和結果就不一樣了?我來算一下:
  • 單片機進位轉換
    我們三次計算依次得到餘數分別是:0、1、1,將所有餘數倒序排列,那就是:110了!   6轉換成二進位,結果是110。二 ----> 十  二進位數轉換為十進位數   二進位數第0位的權值是2的0次方,第1位的權值是2的1次方……   所以,設有一個二進位數:0110 0100,轉換為10進位為:
  • 二進位、八進位、十進位、十六進位之間的轉換
    反過來,當我們看到 FD時,如何迅速將它轉換為二進位數呢?先轉換F:        看到F,我們需知道它是15(可能你還不熟悉A~F這六個數),然後15如何用8421湊呢?應該是8 + 4 + 2 + 1,所以四位全為1 :1111。接著轉換 D:        看到D,知道它是13,13如何用8421湊呢?
  • 二進位與八進位互相轉換
    上節課我們學習了二進位轉換十進位,那二進位與常用的八進位之間是如何互相轉換的呢?下面我們先看看二進位與八進位的轉換方法。
  • 微課 | 計算機中的進位轉換
    對於任何一種進位——n進位,就表示每一位置上的數運算時都是逢n進一位。十進位是逢十進一,十六進位是逢十六進一,二進位就是逢二進一,以此類推,n進位就是逢n進位。進位計數制兩個基本因素「基數」與「位權」構成。基數是指,進位計數制中所採用的數碼(數制中用來表示「量」的符號)的個數。
  • Day7 進位的表示與轉換
    這節課的內容太簡單了……學習進位的轉換。在計算機中,我們常用的進位有4種。一、二進位(Binary)在二進位裡,只有 0 和 1,二進位的運算遵循逢二進一。在 python 中要輸入一個二進位數,你不能直接輸入。比如我輸入一個 666 看看。很明顯這是個十進位。
  • 16進位數轉換成8421BCD編碼函數
    3、BCD碼的加減運算 由於編碼是將每個十進位數用一組4位二進位數來表示,因此,若將這種BCD碼直接交計算機去運算,由於 計算機總是把數當作二進位數來運算,所以結果可能會出錯 解決的辦法是對二進位加法運算的結果採用「加6修正,這種修正稱為BCD調整。即將二進位加法運算的結果修正為BCD碼加法運算的結果,兩個兩位BCD數相加時,對二進位加法運算結果採用修正規則進行修正。修正規則: (1)如果任何兩個對應位BCD數相加的結果向高一位無進位,若得到的結果小於或等於9,則該位不需修正;若得到的結果大於9且小於16時,該位進行加6修正。