IEEE 754 binary32 單精度的浮點數值恰好由 8 個十六進位數字表示,而 binary64 雙精度的浮點數值則恰好由 16 個十六進位數字表示,最高有效位元組寫在最前面。若要把十六進位轉換成 IEEE 754 浮點數,請將該位元串拆成三個欄位:一個前置的正負號位元、一個偏置指數欄位,以及一個尾數欄位,然後代入公式「正負號 × 1.尾數 × 2^(指數 − 偏置值)」進行計算。IEEE 754 標準定義 binary32 的偏置值為 127,binary64 的偏置值為 1023,並且對每一個正規數都會在尾數前面隱含地加上一個前置的 1。特殊位元模式涵蓋零、次正規數、無限大以及 NaN。若不想自己撰寫解析器,又想可靠地把十六進位轉成 IEEE 754 浮點數,可以將十六進位輸入到 IEEE 754 Floating Point Converter,該工具會透過 JavaScript 用於 binary32 與 binary64 交換的同一個 DataView 流程寫入那些完全相同的位元,並回報解碼後的數值、正負號碼、指數、尾數、分類以及未偏置的指數。
十六進位之所以會成為 IEEE 754 交換的標準載體,是因為每一個十六進位數字都對應到四個二進位數字。這種對應關係讓 32 位元浮點數恰好變成 8 個十六進位字元,64 位元浮點數恰好變成 16 個十六進位字元,而不會對前文導零或位元組順序產生任何模糊空間。當某個檔案格式、網路通訊協定或編譯後的二進位檔儲存一個浮點數時,它儲存的就是那些位元,而當你用除錯器、十六進位編輯器或日誌工具傾印那些位元組時,所看到的正是十六進位的表示方式。顛倒這個過程,便是「convert hex to IEEE 754 floating point」這類查詢背後每天都在進行的例行工作,下面的規則讓你可以自行讀取這些位元,或交給會讀取的工具來處理。

IEEE 754 十六進位字串實際上編碼了什麼
IEEE 754 十六進位字串是一組緊密打包的位元樣式,而不是某個數字喬裝而成的樣子。文字「4029000000000000」並不表示十進位的 4.029 × 10^15;它代表的是 01000000 00101001 00000000 00000000 00000000 00000000 00000000 00000000 這 64 個位元,而這些位元在套用 IEEE 754 的規則後,會解碼成浮點數值 12.5。同樣的提醒也適用於較短的十六進位數值:「3F800000」作為 binary32 浮點數剛好就是 1.0,而不是帶有尾端零的十進位數。將十六進位讀成緊密打包的位元樣式,是唯一與檔案、線路格式或硬體暫存器實際儲存內容相符的解讀方式。
兩種寬度值得明確區分。較窄的寬度是 binary32,也稱為單精度,具有 8 個十六進位數字,偏置值為 127。較寬的寬度是 binary64,也稱為雙精度,具有 16 個十六進位數字,偏置值為 1023。把這兩種寬度搞混,是十六進位轉成浮點數結果看起來錯了好幾個數量級最常見的唯一原因,因為指數欄位倍增的同時假數卻維持不變,可能讓數值偏移超過十億倍。如果某個十六進位字串短於 8 個數字,它就不可能是完整的 binary32 樣式;如果它的長度介於 9 到 15 個數字之間,則無法乾淨地符合任何一種標準寬度,因此嚴格的解碼器會拒絕輸入,而不是用零亂位加以填補。
把位元拆成正負號、指數與尾數
一旦你拿到完整的 8 或 16 位數十六進位樣式,就把底層的位元拆成三個連續的欄位。最左邊的位元是正負號:0 代表正,1 代表負。接下來是偏置指數欄位,binary32 寬度為 8 位元,binary64 寬度為 11 位元。剩下的位元就是尾數,binary32 為 23 位元,binary64 為 52 位元。未偏置的指數等於偏置值減去 127(binary32)或減去 1023(binary64)。尾數會被讀成一個二進位數值,並且在二進位小數點左側為每一個正規數隱含地放上一個前置的 1,產生一個介於 1.0 與略低於 2.0 之間的假數。解碼後的值就是「正負號 × 假數 × 2^未偏置指數」。
特殊樣式可以略過這個公式。指數全為零且尾數也全為零時,根據正負號位元決定是 +0 或 −0;在一般比較運算中兩者相等,但在倒數之類的運算中則不相同。指數全為零但尾數不為零時,屬於次正規數,其假數使用 0.尾數 而非 1.尾數,且未偏置的指數為偏置值減一,使得最小的次正規數仍會產生正值。指數全為 1 且尾數為零時,是 +∞ 或 −∞。指數全為 1 且尾數不為零時,是 NaN,即使輸入位元中仍可看到不同的承載資料,JavaScript 會把每一個 NaN 都呈現為單一的 NaN 值。
一個完整的算例可以讓欄位運算更具體。取十六進位字串 4029000000000000 作為 binary64 樣式。其二進位形式為 01000000 00101001 00000000 00000000 00000000 00000000 00000000 00000000。正負號位元為 0,指數欄位 10000000010(二進位)等於 1026(十進位),而 1026 減去偏置值 1023 會得到未偏置指數 3。尾數欄位以位元 1001 開頭,後接零,即二進位尾數 0.1001,等於 1/2 + 1/16 = 0.5625。加上隱含的前置 1 之後,假數變成二進位的 1.1001,也就是十進位的 1.5625。乘以 2^3 得到 1.5625 × 8 = 12.5。將同一個十六進位字串餵給 IEEE 754 Floating Point Converter,會回報儲存值 12.5,並一併顯示正負號碼、指數、尾數、分類以及完整的 64 位元樣式。
如何將十六進位轉換為 IEEE 754 浮點數
解碼流程分為三個簡短的步驟:選擇與十六進位字串相符的位元寬度、輸入十六進位、然後讀取儲存值以及工具所揭露的每一個欄位。IEEE 754 Floating Point Converter 實際使用的完整步驟如下。
- 選擇 bits-or-hex-to-decimal 模式,若十六進位字串為 8 位數則選 binary32,若為 16 位數則選 binary64。
- 將十六進位字串(可加可不加 0x 前置)輸入到十六進位欄位。所選寬度的輸入必須剛好是 8 或 16 位十六進位數字;長度不符的字串會被拒絕,而不是被截斷。
- 讀取儲存值、正負號位元、偏置指數、未偏置指數、尾數、分類(零、次正規、正規、無限大或 NaN),以及完整的二進位樣式。若需要把二進位或十六進位貼到另一個工具,請複製後再貼,並在使用該值進行通訊協定或檔案處理之前,先與實際消費端的位元組順序進行核對。
若你手上是十進位值需要編碼,請改將工具切換到 decimal-to-bits 模式,選擇 binary32 或 binary64,以一般十進位表示法輸入十進位值(可選擇性地加上正負號與指數),然後讀取十六進位輸出以及儲存值,即可看出當 binary64 輸入縮減為 binary32 時所發生的捨入行為。
在十六進位中比較 binary32 與 binary64
這兩種寬度共用相同的整體配置,但在指數範圍與假數精度之間做了取捨。下表摘要整理了驅動每一次十六進位轉浮點數的 IEEE 754-2019 欄位。
| 欄位 | binary32 (單精度) | binary64 (雙精度) |
|---|---|---|
| 總位元數 | 32 | 64 |
| 十六進位數字數 | 8 | 16 |
| 正負號位元數 | 1 | 1 |
| 指數位元數 | 8 | 11 |
| 尾數位元數 | 23 | 52 |
| 指數偏置值 | 127 | 1023 |
| 最小正次正規數 | 2−149 | 2−1074 |
| 最大有限值 | (2 − 2−23) × 2127 | (2 − 2−52) × 21023 |
以上是由 IEEE 754-2019 標準所定義的欄位寬度與偏置值。它們是格式本身的常數,並非由工具計算出來,因此同一張表也適用於任何 IEEE 754 binary32 或 binary64 交換。讀取十六進位樣式時選錯列,是誤判數值最快的途徑,因此這張表值得放在解碼步驟旁邊隨時參考。
為什麼位元組順序與精度對十六進位解碼很重要
十六進位文字以最高有效位元組優先書寫,因而消除了位元組順序的歧義,但當十六進位是來自原始記憶體的傾印時,底層儲存的位元組順序仍然至關重要。大多數檔案格式與網路通訊協定在 IEEE 754 交換中使用 big-endian 位元組順序,而 IEEE 754 Floating Point Converter 所使用的 JavaScript DataView 流程同樣以 big-endian 順序讀寫,因此直接從 big-endian 來源取得的十六進位字串可以直接解碼。little-endian 來源只有在位元組反轉之後才會產生相同的十六進位字串,而交換位元組順序會改變浮點數的每一個位元,除了量級之外往往連正負號也會改變。
精度是十六進位字串所承載的第二個契約。Binary64 具有 52 個尾數位元,大約等同於 15 到 17 個有效十進位數字;而 binary32 僅有 23 個尾數位元,僅能提供約 6 到 9 個有效十進位數字。轉換器所回報的儲存值反映的是捨入後的二進位值,而非你原本輸入的十進位字串,因此輸入 0.1 會被捨入到鄰近的 binary64 值,而無法還原為無限的 0.0001100110011... 展開式。在處理金額、識別碼以及任意精度運算時,請使用十進位或整數運算函式庫,而不是反解十六進位浮點數,因為捨入已經發生,無法藉由更換工具來還原。
瀏覽器型十六進位解碼的限制
IEEE 754 Floating Point Converter 完全在瀏覽器中執行,並使用與 JavaScript 執行環境相同的 DataView 流程,因此對於任何給定的位元樣式,它與 JavaScript Number 所能容納的值一致。這種對稱性帶來三項實際限制。首先,不同的 NaN 承載資料無法透過數值加以區分,因為 JavaScript 會把每一個 NaN 折疊成單一值,即使輸入位元在顯示畫面中仍可看到。其次,轉換器會回報無限大與 NaN 的分類,而不是自行捏造一個未偏置指數,因為對這些樣式而言未偏置指數是未定義的。第三,輸入解析器會拒絕十六進位數字字面值、運算式、逗號、單位以及地區特有的十進位分隔符,以維持解析的明確性;這代表在二進位輸入中出現多餘的「0x」前置,或在尾數中出現逗號,都會產生錯誤,而不是悄悄被強制轉換。針對特定格式中規範十六進位交換的位元組順序、精度與分類規則,請把位元樣式與將會消費該資料的語言、檔案格式、網路通訊協定或硬體規格相互對照查驗。
在 decimal-to-bits 方向上,同一個工具也會揭露 binary64 輸入縮減為 binary32 時所發生的捨入行為,並接受以十進位形式表示的 Infinity、-Infinity 與 NaN,以及一般十進位表示法。解碼與編碼的完整覆蓋範圍,讓 IEEE 754 Floating Point Converter 成為在十六進位交換樣式與瀏覽器浮點數之間來回轉換時唯一可參考的本地工具,並且當輸入是十進位值而非十六進位位元樣式時,可與 將十進位數字轉換為 IEEE 754 浮點數格式的深入說明自然搭配使用。
若想進一步了解,請參閱 如何將十六進位 IP 位址轉換為點分十進位。