二進位轉換

Binary
10 1010
Octal
52
Decimal
42
Hexadecimal
2a

二補數

8-bit
0010 1010
16-bit
0000 0000 0010 1010
32-bit
0000 0000 0000 0000 0000 0000 0010 1010
64-bit
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010 1010

您輸入的內容不會被儲存、記錄或分享。

在二進位、八進位、十進位與十六進位之間轉換數值,也可將文字轉為二進位或反向轉換。即使遠超過 64 位元也保持精確。

使用方式

  1. 選擇您輸入時使用的進位,並輸入數值。
  2. 其他所有進位會同時顯示,下方並附上二補數。
  3. 切換到「文字」模式即可在文字與二進位之間轉換。

為什麼計算是精確的

這裡的每一次轉換都使用 BigInt,它沒有大小上限,也不會四捨五入。這聽起來像是實作上的附註,實際上卻是本工具與網路上大多數轉換器最主要的差別。

JavaScript 的數字是 IEEE 754 雙精度浮點數,只能精確表示到 9,007,199,254,740,991,也就是 2 的 53 次方減一。用直覺方式寫的轉換器,在這個值以下是正確的,超過之後就會默默出錯。這是最糟糕的一種失敗:沒有錯誤、沒有警告,只有一個長度和形狀都對、但剛好不是正確答案的結果。

這個門檻低到經常會碰到。完整的 64 位元值遠遠超過它;Snowflake 產生的資料庫 ID、奈秒時間戳記、硬體暫存器傾印、雜湊值,以及從 64 位元記憶體位址讀出的任何值也都超過。如果你要轉換的數字大到值得一轉,一般的轉換器正好在這裡開始變得不可靠。

二補數

四種進位的下方,會顯示該值以有號 8、16、32 或 64 位元整數儲存時的樣子。這和在二進位數字前面加上負號並不相同,而正是這個差別讓人在讀記憶體時卡住。

在二補數中,負數的做法是把其絕對值的所有位元反轉再加一。在 8 位元中,-1 是 11111111,-128 是 10000000。沒有可以單獨拿掉的符號位元,沒有負零,而且加法不需要特別處理正負號,這就是為什麼所有主流處理器都以這種方式儲存整數。

這也解釋了範圍為何不對稱。8 位元能表示的是 -128 到 127,而不是 -128 到 128,因為零占用了正數那一側的一個位置。如果數值放不進所選的位元寬度,工具會直接說明,而不是默默截斷,因為截斷後的結果看起來完全合理,實際上卻是錯的。

解讀輸出

較長的數值會分組顯示,方便一眼讀懂:二進位每 8 位一組,十六進位兩兩一組,十進位每三位一組。分組只影響顯示,複製時會自動移除。

輸入也刻意朝同樣的方向寬容。0x、0b 與 0o 前綴都能接受並忽略,作為分隔符號的底線、空格與逗號也一樣,所以從原始碼或十六進位傾印中直接貼上的字面值,不必先清理就能使用。

文字轉二進位

文字模式會先把文字編碼為 UTF-8,再把每個位元組寫成 8 個位元。反向轉換時,位元會被讀回位元組,再以 UTF-8 解碼。

另一種做法是直接取每個字元的碼元,這是多數文字轉二進位工具的做法,但它會在兩個地方出錯。任何超過 U+00FF 的字元都會產生超過 8 個位元,完全無法對齊位元組。更糟的是,基本多文種平面以外的字元,包括大部分表情符號,在 JavaScript 中是以兩個代理碼元儲存;如果個別處理,就會變成兩個毫無意義的半邊,在另一端解碼不出任何東西。透過 UTF-8 可以同時避開這兩個問題,這就是為什麼表情符號在這裡能來回轉換,在其他地方卻常常不行。中文字在這裡同樣能正確來回轉換。

同一個數字的四種寫法

以 2,025 為例。二進位是 11111101001,八進位是 3751,十六進位是 7E9。數量本身沒有改變,改變的只是書寫時使用的基數。任何基數的每個位數都代表位值:7E9 是 7×256 加 14×16 加 9,而 11111101001 是所有被設定的位元所代表的 2 的次方之總和。

十六進位之所以重要,是因為 16 是 2 的四次方,所以一個十六進位數字剛好等於四個位元,而且對應關係好記:F 是 1111,8 是 1000,A 是 1010。這就是為什麼記憶體傾印、色彩值、雜湊與 MAC 位址都以十六進位而非十進位書寫——你可以直接從數字讀出位元,不必做任何換算。

八進位主要留存於一個地方:Unix 檔案權限,原因相同:一個數字對應三個位元,剛好對應讀取、寫入與執行,所以 755 就是 rwxr-xr-x,什麼都不必查。

位元運算,以及遮罩的用途

一旦把值看成二進位,位元運算子的意義就很明顯。AND 只保留兩個運算元都為 1 的位元,這正是測試旗標的方法:value AND 0b0100 不為零,代表第三個位元被設定。OR 用來設定位元,XOR 用來翻轉,NOT 反轉全部,而左移或右移則是乘以或除以 2 的次方。

這就是為什麼旗標會以單一位元儲存,而不是分開的布林值。權限欄位、一組功能開關,或網路封包上的選項,都能放進一個整數中,而檢查其中一項只需要一道指令。與之做 AND 的常數稱為遮罩,用二進位或十六進位書寫能讓它的用途一目了然,這是十進位寫法永遠做不到的。

位移有一個值得知道的陷阱:在多數語言中,對有號值做右移會保留符號位元,所以 -8 右移後是 -4,而不是一個很大的正數。同時提供兩種行為的語言會用不同的寫法,例如 JavaScript 的 >>> 運算子。

一個數字在記憶體中的樣子

寫成 7E9 的值,並不會告訴你它是怎麼儲存的。寬度很重要:同一個數字依型別不同會占用一、二、四或八個位元組,而超出寬度的值不會自動變大,而是被截斷或繞回。

位元組順序同樣重要。小端序機器(也就是你會遇到的幾乎所有機器)先儲存最低有效位元組,所以 32 位元值 0x000007E9 在十六進位傾印中會呈現為 E9 07 00 00。大端序則相反,而網路協定規定的正是大端序,這也是它被稱為網路位元組序、以及每個 socket 邊界都有轉換函式的原因。不知道自己看的是哪一種就讀傾印,只會得到自信滿滿的胡說八道。

同樣寬度的無號與有號型別存放相同的位元,卻做不同的解讀:0xFF 當作無號位元組是 255,當作有號位元組則是 -1。位元組本身不會告訴你哪一種才正確,決定的只有型別。

浮點數是完全不同的系統

以上所談的都是整數。倍精度浮點數由一個符號位元、11 位元指數與 52 位元尾數組成,也就是以「分數乘以 2 的次方」來表示一個數。無法被精確表示的值,會被捨入到最接近的可表示值。

這就是「0.1 加 0.2 不等於 0.3」這個著名結果的全部解釋。十分之一在二進位中是循環小數,就像三分之一在十進位中一樣,所以它被略帶誤差地儲存,兩個略帶誤差的值相加,結果自然也略帶誤差。運算是正確的;錯的是以為十進位小數能毫髮無傷地通過二進位儲存這個期待。

實務上的規則也隨之而來:永遠不要用浮點數儲存金額,請改用整數的分或十進位型別;比較浮點數時要使用容差,而不是相等;並且要知道倍精度中超過 2^53 的整數會失去精度,這也是本轉換工具改用任意精度運算的原因。

你會在哪裡實際遇到這些基數

只要有位元組要呈現給人看,就會出現十六進位。CSS 的色彩是三個位元組的十六進位;MAC 位址是六個;SHA-256 摘要是三十二個,寫成六十四個字元。記憶體位址、作業系統與硬體的錯誤碼也都是十六進位,因為位元邊界正好對齊數字。

二進位則出現在個別位元帶有意義的地方:網路遮罩、硬體暫存器、權限位元、功能旗標,以及 QR 碼與 UTF-8 這類格式的編碼表。讀 UTF-8 是個好例子:以 0 開頭的位元組是 ASCII,以 110 開頭的是兩位元組序列的起頭,以 10 開頭的則是後續位元組。這個結構在十進位中看不見,在二進位中卻一目了然。

八進位大致退守到檔案權限與少數舊格式,但仍留下一個值得記住的陷阱:在 C、ES5 之前的 JavaScript 與若干設定格式中,開頭為零的數字會被當成八進位,所以 0755 並不是七百五十五。現代 JavaScript 改用明確的 0o 前綴,而 YAML 1.2 則完全取消了開頭零的規則——這本身又成為另一種驚訝的來源,因為檔案權限可能被讀成十進位數字。

常見問題

處理非常大的數字時仍然準確嗎?
是。所有運算都使用 BigInt,任何位數都精確。以一般 JavaScript 數值實作的轉換器在超過 2^53(約九千兆)後就會失去精度,接著給出看似合理但錯誤的答案。完整的 64 位元數值已經超出該上限,因此這個差異比想像中更常遇到。
什麼是二補數?為什麼要顯示?
它是有號整數在記憶體中實際儲存的形式。在 8 位元二補數中,-1 是 11111111,而不是符號位加上 1。閱讀暫存器內容或除錯通訊協定時,需要看的正是這個儲存形式。若某個位寬容納不下該數值,會直接標示而不會無聲截斷。
可以貼上含 0x 或 0b 前綴的值嗎?
可以。這些前綴會被接受並忽略,用於分組的底線、空白與逗號也一樣,因此可以直接從程式碼貼過來。
文字如何轉換成二進位?
先將文字編碼為 UTF-8,再把每個位元組寫成八個位元。因此中文、日文、韓文與表情符號都能正確往返。直接讀取字元碼的工具在 ASCII 之外會出錯,單一表情符號會變成兩段損壞的資料。

最後更新:

回報問題

請提供操作步驟、預期結果及實際結果。請移除機密與個人資料。

相關工具