NaN-Boxing:在 JavaScript 的非數值中走私資料的巧妙技術

BigGo Editorial Team
NaN-Boxing:在 JavaScript 的非數值中走私資料的巧妙技術

在程式設計世界中,開發者經常尋找創新方式來最佳化程式碼並實現巧妙的技術駭客。最近引起開發者社群關注的一種技術就是 NaN-boxing —— 一種利用 JavaScript 中非數值(Not-a-Number,NaN)特性來儲存額外資料的方法。

什麼是 NaN-Boxing?

NaN-boxing 是一種利用浮點數在記憶體中按照 IEEE 754 標準表示方式的技術。在 JavaScript 中,所有數字都是 64 位浮點值,由符號位、指數和尾數(小數部分)組成。當數學運算失敗時(比如零除以零),它們會產生特殊的 NaN 值。有趣的是,雖然 NaN 表示無效操作,但它們的尾數部分可以包含各種位模式,為創造性的資料走私打開了大門。

「這通常被稱為 NaN-boxing,常用於實現動態語言。」

這種技術之所以有效,是因為 IEEE 754 規範允許多種 NaN 的表示方式。只要指數位全部設為 1,且小數部分至少有一位非零,該值就被視為 NaN。這就留下了大量可用於編碼其他資訊的位,同時保持 NaN 狀態。

IEEE 754 浮點數結構

  • 符號位:1 位
  • 指數:11 位
  • 尾數/小數部分:52 位

NaN 特性

  • 指數字段:所有位都設為 1
  • 小數字段:至少有一位必須為非零
  • 結果:在保持 NaN 狀態的同時,大約有 52 位可用於資料儲存

瀏覽器相容性

  • Firefox:規範化從 ArrayBuffers 中提取的 NaN
  • 其他瀏覽器:可能允許自定義 NaN 位模式

類似技術

  • 在無鎖演算法中使用 64 位整數的高位儲存輔助資料
  • OCaml 的 63 位整數(1 位保留用於垃圾回收)

瀏覽器相容性挑戰

並非所有 JavaScript 引擎都以相同方式處理 NaN,這創造了有趣的相容性挑戰。例如,Firefox 會在從 ArrayBuffers 提取 NaN 值時對其進行標準化。這可能是因為 Firefox 的 SpiderMonkey JavaScript 引擎在內部使用了 NaN-boxing,並且沒有辦法表示非規範的 NaN 浮點數。這一限制意味著依賴自定義 NaN 值的技術可能無法在所有瀏覽器中一致工作。

實際應用

NaN-boxing 不僅僅是一種好奇心驅動的技術——它在語言實現中有實際應用。幾種動態程式語言使用這種技術來高效表示值。透過使用原本在 NaN 值中被浪費的位,語言可以直接在看似是數字的結構中編碼型別資訊和小值,避免額外的記憶體分配。

一些開發者已經找到了這種技術的創造性用途,包括資料壓縮、隱寫術(在其他資料中隱藏資訊)以及最佳化效能關鍵應用程式的記憶體使用。文章中提到的 stuffed-naan 專案幽默地展示了這一概念,儘管它對壓縮比和隱私優勢的宣告帶有調侃性質。

技術背景

從本質上講,NaN-boxing 利用了 IEEE 754 浮點數的結構。當指數字段全為 1,且小數字段至少有一位被設定時,無論小數中的具體位模式如何,該數字都會被解釋為 NaN。這給開發者提供了大約 52 位的空間來儲存任意資料,同時保持 NaN 分類。

這種技術類似於系統程式設計中使用的其他位操作技巧,例如在無鎖演算法中使用 64 位整數的最高位來儲存輔助資料。例如,OCaml 預設使用 63 位整數,保留最後一位以幫助垃圾收集。

雖然 NaN-boxing 可能看起來像一種晦澀的駭客技術,但它代表了推動計算發展的創造性思維。透過理解資料在最低層次表示的複雜性,開發者可以找到意想不到的方式來最佳化效能併為複雜問題實現優雅的解決方案。

參考:Stuffed-Na(a)N: stuff your NaN s