讀懂代碼裏的魔法數字
& 0xFF、>>> 24、| (1 << 3)、0o644——學完第 11–15 關,這些片段各自在取哪幾位、置哪一位,一眼可見。
使用指南
進制與位運算課是一門 17 關的交互式教程,把「二進制」從抽象概念變成手能摸到的開關:輸入一個數,二、八、十、十六進制四種寫法同步變化;點亮或熄滅某一位,數值立刻更新;兩個數做 AND / OR / XOR / 移位,結果逐位對齊顯示;輸入一個小數,看它被拆成符號、指數、尾數三個字段,以及它在雙精度裏精確等於哪個十進制小數。每一關一個具體目標——換算某個數、湊出 200、寫出 -5 的補碼、算出 5 << 3——判定器即時反饋。
更新於 2026-09-094 个来源约 13 分钟读完
進制與位運算課是一門 17 關的交互式教程,把「二進制」從抽象概念變成手能摸到的開關:輸入一個數,二、八、十、十六進制四種寫法同步變化;點亮或熄滅某一位,數值立刻更新;兩個數做 AND / OR / XOR / 移位,結果逐位對齊顯示;輸入一個小數,看它被拆成符號、指數、尾數三個字段,以及它在雙精度裏精確等於哪個十進制小數。每一關一個具體目標——換算某個數、湊出 200、寫出 -5 的補碼、算出 5 << 3——判定器即時反饋。
課程分四段:進制換算(二 ↔ 十、2 的冪、十六進制與八進制為什麼是「速記」);有符號數(補碼怎樣讓減法變加法、溢出為什麼讓 127 + 1 變成 -128);位運算(AND 掩碼、OR 置位、XOR 翻轉、移位乘除,以及它們在 Linux 權限位 755 裏的直接應用);浮點數(IEEE 754 的字段佈局,與 0.1 + 0.2 !== 0.3 的真正原因)。
約 45 分鐘學完。面向初學編程的人、準備面試的開發者、以及每次看到 0x1F、& 0xFF、chmod 644 都要查一遍的人;不需要數學基礎,會加法即可。
0x。?lesson=n 可直達某關。本頁上方就是課程本體:改數值、點位開關、填答案。
第 9 關「求補碼」要求寫出 -5 的 8 位補碼:
第 16 關「IEEE 754 字段」輸入 0.15625,面板顯示:
絕對值 5 0 0 0 0 0 1 0 1
按位取反 1 1 1 1 1 0 1 0
加一 1 1 1 1 1 0 1 1 ← 答案 11111011
驗證 無符號 251,有符號 251 − 256 = −5 ✓0.15625 = 0.00101₂ = 1.01₂ × 2⁻³
符號 0
指數 −3 + 127 = 124 = 01111100 ← 答案 124
尾數 01000000000000000000000(隱含前導 1)
32 位 0 01111100 01000000000000000000000 = 0x3E200000任何進制都是「位權求和」。十進制 203 = 2×100 + 0×10 + 3×1;二進制 1011 = 1×8 + 0×4 + 1×2 + 1×1 = 11。從右往左位權依次是 1、2、4、8、16、32、64、128……二進制轉十進制就是把為 1 的位權加起來;十進制轉二進制則反覆除 2 記餘數,或者從大到小貪心減去 2 的冪(第 7 關的位開關就是這個過程的可視化)。
,與 1000 只差 2.4%,所以早期用 KB 表示 1024 字節。這造成了至今的混亂:硬盤廠商按 標 1 GB,操作系統按 算,500 GB 的盤顯示 465 GiB。IEC 為此定義了 KiB、MiB、GiB(二進制前綴),Linux 與 macOS 已逐步採用,Windows 仍顯示 GB 卻按 GiB 計算。記住幾個數:、、、。
二進制太長,人讀不了。十六進制(0–9 加 A–F)用一個符號表示 4 位,一個字節恰好兩位:11111111 = FF,10110110 = B6。換算不需要經過十進制——把二進制從右往左每 4 位切一段、每段查表即可,第 5 關就練這個。八進制用一個符號表示 3 位,歷史上用於 12、24、36 位字長的機器,今天主要活在 Linux 權限(755)和 C 的 \012 轉義裏。程序裏的前綴:0x 十六進制、0o 八進制、0b 二進制。
每個非負整數只有一種二進制寫法,所以一個 8 位數可以當 8 個互不干擾的開關(標誌位)用:flags = READ | WRITE、if (flags & EXEC)。CSS 的 font-weight、Unix 的文件權限、網絡協議頭裏的標誌字段都是這樣打包的。第 7 關要你只點開關湊出 200——從最高位貪心:200 ≥ 128 點亮,剩 72 ≥ 64 點亮,剩 8 = 2³ 點亮,得到 11001000。
負數怎麼存?直覺是拿一位當符號,但那樣會有「+0」和「−0」兩個零,加法電路也要分情況。補碼(two's complement)的規定是:最高位為 1 表示負數,數值等於無符號值減 。8 位裏 11111111 是 255,也是 255 − 256 = −1;10000000 是 −128。求一個負數的補碼:寫出絕對值、按位取反、加一——−5 就是 00000101 → 11111010 → 11111011。
補碼的精妙之處在於加法不需要知道正負:−5 + 5 = 11111011 + 00000101 = 1 00000000,第 9 位進位丟棄後正好是 0。減法於是變成「加上補碼」,CPU 只需要一個加法器。8 位補碼的範圍是 −128 到 127:負數比正數多一個,因為 0 佔了正數一側的位置。
位寬有限,加過頭就繞回去。int8 裏 127 = 01111111,加一得 10000000,按補碼解讀是 −128。C、Java、Go 的整數溢出都是這樣靜默發生的;JavaScript 的普通 Number 是浮點數所以不會溢出,但 Int8Array、|0 與所有位運算都在 32 位補碼上進行,(2 ** 31) | 0 會得到 −2147483648。著名的 2038 年問題是 32 位有符號秒數在 2038-01-19 03:14:07 UTC 溢出為負;早年遊戲的「分數變負」「金幣歸零」也是同一回事。
三種按位邏輯運算各有固定用途:
| 運算 | 規則 | 慣用法 |
|---|---|---|
AND & |
兩位都 1 才 1 | 掩碼取位:x & 0x0F 取低 4 位;IP & 子网掩码 得網絡地址;n & 1 判奇偶 |
OR | |
任一位 1 就 1 | 置位:flags | READ 打開一個標誌而不影響其它 |
XOR ^ |
兩位不同才 1 | 翻轉 / 還原:x ^ m ^ m = x;一次性密碼本、校驗和、無臨時變量交換 |
「關閉」某位是 AND 與 NOT 的組合:flags & ~FLAG。XOR 的兩個恆等式 、 是它一切用途的根源:a ^= b; b ^= a; a ^= b 交換兩個數,找出數組裏唯一齣現奇數次的元素只需把所有數 XOR 起來。
左移 n 位等於乘 (低位補 0),右移 n 位等於除以 向下取整。5 << 3 = 101000 = 40。移位是最便宜的乘除法,更常見的用途是把小字段打包進一個整數:(r << 16) | (g << 8) | b 把三個 0–255 拼成 24 位顏色值,反過來 (rgb >> 8) & 0xFF 取出 G。注意右移有兩種:算術右移 >> 保留符號位(負數仍是負數),邏輯右移 >>> 補 0。JavaScript 的移位在 32 位上進行,1 << 31 得到 −2147483648,1 << 32 等於 1 << 0 = 1,因為移位數按模 32 處理。
Linux 文件權限是 9 個位:所有者、組、其他人各 3 位,依次是讀 r = 4、寫 w = 2、執行 x = 1。每組三位求和就是一位八進制:rwxr-xr-x = 7、5、5 → 755;rw-r--r-- = 644。這是位權與掩碼在日常運維裏最直接的應用:chmod u+x 是 OR 上 0o100,chmod o-w 是 AND 上 ~0o002。前面還可能有第 10–12 位(setuid、setgid、sticky),所以你有時會見到四位數 4755。
浮點數是二進制的科學計數法:
單精度 32 位 = 1 位符號 s + 8 位指數 e(偏移 127)+ 23 位尾數 f;雙精度 64 位 = 1 + 11(偏移 1023)+ 52。尾數只存小數部分,整數部分的 1 是隱含的,白賺一位精度。指數加偏移量存儲是為了讓浮點數按位比較大小時和整數一樣單調。0.15625 = = ,指數字段是 −3 + 127 = 124 = 01111100,尾數字段是 01 後面補零。
幾個特殊編碼:指數全 0 是非規格化數(表示極接近 0 的值和 ±0);指數全 1 且尾數全 0 是 ±∞;指數全 1 且尾數非 0 是 NaN——這就是為什麼 0/0 是 NaN 而 1/0 是 Infinity,也是為什麼 NaN 有很多種位模式卻都「不等於自己」。
二進制小數只能精確表示 2 的負冪之和:0.5、0.25、0.125…… 在二進制裏是無限循環小數 ,52 位尾數必須舍入。於是雙精度裏的 0.1 精確地等於
略大於 0.1;0.2 同樣略大;兩者相加再舍入得到 0.3000000000000000444…,而字面量 0.3 最近的雙精度值是 0.29999999999999998889…——兩個不同的二進制數,所以 0.1 + 0.2 === 0.3 為 false。這不是 JavaScript 的 bug,任何用 IEEE 754 的語言(Python、Java、C、Go)都一樣。
對策取決於場景:錢用整數「分」或十進制庫(decimal、BigDecimal、Decimal.js);比較用誤差範圍 Math.abs(a − b) < ε;顯示用 toFixed(2) 只在最後一步取整;計數用整數,雙精度能精確表示 以內的所有整數,超過就該用 BigInt。課程最後一關的選擇題「存的 0.1 比真值大還是小」,答案是「大」——看精確展開第 18 位以後的 55511… 就知道。
BigInt 的位運算。Math.fround、半精度與 bfloat16。0x。10000000 是 −128 而不是 −0,−1 是全 1。>> 就是除以 2」:對負數是向下取整而不是向零取整:−5 >> 1 得 −3,Math.trunc(−5 / 2) 得 −2。toFixed 就能解決金額問題」:toFixed 只是顯示取整,中間運算的誤差已經累積;金額從一開始就該用整數分。Int32Array、| 0 都在 32 位補碼上運行,會溢出;Number 超過 會丟失整數精度。& 0xFF、>>> 24、| (1 << 3)、0o644——學完第 11–15 關,這些片段各自在取哪幾位、置哪一位,一眼可見。
第 10 關的溢出與第 17 關的舍入解釋了絕大多數這類 bug:前者換更寬的類型或 BigInt,後者換整數分或十進制庫。
子網掩碼就是 AND 掩碼(第 11 關):192.168.1.37 & 255.255.255.0 = 192.168.1.0;chmod 755 / 644 的含義與「為什麼腳本沒有執行權限」(第 15 關)。
補碼、溢出、XOR 交換、移位乘除、IEEE 754 字段、0.1 + 0.2 是最常被問到的基礎題,課程每關的判定就是一道自測。
大多數關卡允許前導零省略(1011 與 00001011 都對);標明「8 位補碼」的關卡要求恰好 8 位,因為位寬本身就是答案的一部分。
0x?沒有區別,ff、FF、0xFF 都算對。
同一串位的兩種解讀:無符號把最高位當 ,有符號把它當 。11111111 在 8 位下無符號是 255、有符號是 −1。
把雙精度的尾數與指數按定義展開成有理數,再做長除法得到全部十進制位——它不是近似,而是這個二進制數的精確十進制表示。
當前瀏覽器的 localStorage,不上傳;「分享本關」的鏈接只帶關卡號,不帶進度。
課程全部在瀏覽器本地運行:進制換算、位運算、浮點字段拆解與精確展開都是頁面內的純函數計算,不請求服務器;通關進度存在本機 localStorage。
chmod 工具——符號模式與八進制模式的定義:https://pubs.opengroup.org/onlinepubs/9799919799/utilities/chmod.html(访问日期:2026-09-09)更新於 2026-09-09
17 關:二/八/十六進制換算、位開關、補碼與溢出、AND/OR/XOR/移位、chmod 權限位、IEEE 754 拆解與 0.1+0.2 之謎
此工具尚未完整翻譯,部分內容使用源文或回退語言。
目標数值面板里是二进制 1011。它等于十进制多少?
每一位的「位权」是 2 的幂:最右边是 2⁰=1,往左依次 2、4、8、16……把为 1 的位的权相加就是十进制值:1011 = 8 + 0 + 2 + 1 = 11。面板里点任何一位都能立刻看到十进制随之变化——比背口诀更快建立直觉。