二進位轉換
- 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 位元也保持精確。
使用方式
- 選擇您輸入時使用的進位,並輸入數值。
- 其他所有進位會同時顯示,下方並附上二補數。
- 切換到「文字」模式即可在文字與二進位之間轉換。
為什麼計算是精確的
這裡的每一次轉換都使用 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 之外會出錯,單一表情符號會變成兩段損壞的資料。
最後更新:
請提供操作步驟、預期結果及實際結果。請移除機密與個人資料。
