目的: 檢驗學員對 bitops 及數值系統的認知
1
CRC (循環冗餘校驗,Cyclic Redundancy Check) 是種用於網路與儲存設備的錯誤檢測演算法。發送端指定一個數值作為除數,並對訊息進行除法運算來取得餘數,然後將餘數附加到訊息的末尾。接收端對該訊息執行相同的運算來驗證是否有錯誤,如果餘數不為零,則表示訊息有誤。由於這種方法不會修改訊息內容 (冗餘性),且計算方式只是將被除數位移後再相減 (循環碼),因此稱為 CRC。
當 CRC 演算法的除數 (正式名稱為 check value) 長度為 n 個位元時,稱為 n-bit CRC。CRC32C 是 CRC32 的變體,其除數為 32 位元的二進位數。多項式如下:
對應的十六進位表示為 0x1EDC6F41
CRC 不把資料當作數字,而是當作多項式係數來處理。例如,8-bit 資料 11010101
可以表示為:
即:
計算 CRC 時,使用資料的多項式去除以預先指定的 CRC 多項式,取餘數作為校驗碼。例如,給定資料 11010101
(8-bit),若用 0x1EDC6F41
來除,就會得到一個 32-bit 餘數,這就是 CRC32 的值。
CRC 計算時,不是使用標準的數字除法,而是透過 XOR 運算來模擬二進位多項式的除法。最後留下來的 32-bit 餘數,就是 CRC32 校驗碼。
接下來我們採用位元反轉方式。至於使用該方法的原因,可見 Fast CRC32。
最初的實作:
現代編譯器可以將三元運算子最佳化為條件移動 (conditional move,簡稱 cmov) 指令,以避免分支。因此,我們可將 if-else 改為三元運算子:
接著我們可發現 P 的哪些位元會進行 XOR 運算,完全由 crc
較低 8 個位元所決定。因此,可改寫為:
將其重寫為單一運算式:
將 A 至 H 合併為單一值 T,則可簡化為:
我們可預先計算 T,因為它僅由 256 種排列組合組成。知名的模擬器專案 QEMU 就採用該手法,參見 util/crc32c.c
上述方式需要佔用 1 KiB 的空間來保存表格。
參見 Fast CRC32 提到的 "Half-Byte Algorithm",我們可將 8-bit 表格查詢拆分為二次 4-bit 查詢,產生器如下:
以下程式碼 (部分遮蔽) 具備與 crc32_naive
完全等效的行為。
補完程式碼,使其運作符合預期,AAAA 到 DDDD 均為十六進位數值,亦即以 0x
開頭,小寫字母。
延伸問題:
2
以下程式碼實作小型的模組化合成器 (Synthesizer 或 Synth),得以處理多個「聲音」及其內部互連的元件來產生音訊。程式的核心架構是由代表振盪器、envelope、濾波器和混音器的各個節點所構成。每個聲音都包含這些節點,它們連接一起以便產生並調整聲音的特性。
程式碼使用定點數,而用 Q notation,以下是產生正弦波表格的 Python 程式:
該輸出結果導向至 sine-table.h
檔案。
程式碼輸出多種波形:
對應的函式原型:
q15_t sawtooth_wave(q15_t input)
q15_t sine_wave(q15_t input)
q15_t square_wave(q15_t input)
音訊合成器的程式碼可見 main.c (部分遮蔽),編譯並執行:
預期會輸出 out.wav
,其內容為〈一閃一閃亮晶晶〉,可用 MPlayer 一類的工具播放。
補完程式碼以符合預期,作答規範:
0x
開頭),大寫英文字母,最精簡的方式書寫0x
開頭的數值)Q15_
開頭的巨集名稱延伸問題:
3
chat.c (部分遮蔽) 實作精簡的聊天伺服器,可用 TCP 連線工具 (如 telnet) 存取。
作答規範:
延伸問題: