contributed by < cy023
>
1
先計算 a + b
再除以 2
,但有機會在計算 a + b
時就發生溢位,導致計算結果錯誤。
可以避免在 Version 1 中將 a
, b
直接相加溢位而導致的錯誤。
但是使用這個函式前提是需要確保大數小數傳參傳入的順序。
例如呼叫 average(100, 10);
在函式內會計算 (10 - 100)
因為傳參的型態都是無號數 (unsigned) ,所以也會導致預期外的溢位。
試著將傳參改為 int32_t
型態想解決上述問題,但發現 (high - low)
為奇數時,大數小數傳入的順序不同,甚至可能導致輸出結果不同。
eg.
(high - low) < 0
會將 high - low
向上取整。
average21(100, 9);
結果為 55
100 + (9 - 100) / 2 = 100 + (int32_t)-45.5 = 100 + (-45) = 55
(high - low) > 0
會將 high - low
向下取整。
average21(9, 100);
結果為 54
9 + (100 - 9) / 2 = 9 + (int32_t)45.5 = 9 + 45 = 54
bitwise
操作其中 EXP1
為 a
, b
, 1
(數字) 進行某種特別 bitwise 操作,限定用 OR, AND, XOR 這三個運算子。
(a >> 1)
, (b >> 1)
直接將 a
, b
兩數值分別除以 2 (進行 >>
運算),考慮到 a
, b
兩數值為 uint32_t
型態,若無法整除,會有小數被捨去。
如果 a
, b
皆為奇數,計算 與 ,分別會被捨去 0.5 (共捨去 1),因此計算平均時需要將 a
, b
皆為奇數的情況下補回 1。
a & 1
運算可以檢查數字 a
的 LSB 是否為 1,等效於檢查 a
是否為奇數。
根據以上,EXP1
敘述,為當 a
, b
都是奇數時需要補回的數,填入 a & b & 1
檢查 a
, b
是不是都是奇數。
bitwise
操作其中 EXP2
和 EXP3
為 a
和 b
進行某種特別 bitwise 操作,限定用 OR, AND, XOR 這三個運算子。
參考二進位加法的概念。
a
, b
為 1 bita & b
用於檢查 a
, b
是不是同為 1,如果是則需要進位。
a | b | a & b | Description |
---|---|---|---|
0 | 0 | 0 | a + b = 0 不需要進位 |
0 | 1 | 0 | a + b = 1 不需要進位 |
1 | 0 | 0 | a + b = 1 不需要進位 |
1 | 1 | 1 | a + b = 2 需要進位 |
a ^ b
用於將兩數相加,但不會保留進位的資訊。
a | b | a ^ b | Description |
---|---|---|---|
0 | 0 | 0 | a + b = 0 |
0 | 1 | 1 | a + b = 1 |
1 | 0 | 1 | a + b = 1 |
1 | 1 | 0 | a + b = 2 (需要進位變成 ) |
a
, b
為 uint32_t
型態a + b
表示成相似題目表達式的,((a & b) << 1) + (a ^ b)
(可以想成小學的直式加法,需要進位時,在比當前高一位的數字加一)。(a + b) / 2
可以表示成,(((a & b) << 1) + (a ^ b)) >> 1
,簡化成 (a & b) + (a ^ b) >> 1
延伸問題:
移動平均(Moving average),又稱滾動平均值、滑動平均,在統計學中是種藉由建立整個資料集合中不同子集的一系列平均數,來分析資料點的計算方法。
移動平均通常與時間序列資料一起使用,以消除短期波動,突出長期趨勢或周期。短期和長期之間的閾值取決於應用,移動平均的參數將相應地設置。例如,它通常用於對財務數據進行技術分析,如股票價格、收益率或交易量。它也用於經濟學中研究國內生產總值、就業或其他宏觀經濟時間序列。
2
改寫〈解讀計算機編碼〉一文的「不需要分支的設計」一節提供的程式碼 min,我們得到以下實作 (max
):
a > b
max
需要回傳 a
,也就是 a ^ 0
(任意數字對 0
作 XOR 操作,結果與原數值相同)((EXP4) & -(EXP5))
為 0
a < b
max
需要回傳 b
,也就是 a ^ a ^ b
(b
對 a
作亮次 XOR 操作,結果為 b
)((EXP4) & -(EXP5))
為 a ^ b
((EXP4) & -(EXP5))
結構。
EXP4
填入 a ^ b
EXP5
作為 a
, b
兩數值比較的判斷
a > b
-(EXP5)
要是 0
( )a < b
-(EXP5)
要是 -1
( )EXP5
為 a < b
延伸問題:
請在 Linux 核心原始程式碼中,找出更多類似的實作手法。請善用 git log 檢索。
3
u
保持!((u | v) & 1)
判斷 u
, v
是否同為偶數。shift
變數用來紀錄要將後續結果作幾次 *2
(<< 1
) 的運算。延伸問題:
4
參考 Introduction to Low Level Bit Hacks
Bit Hack #7. Isolate the rightmost 1-bit.
y = x & (-x)
This bit hack finds the rightmost 1-bit and sets all the other bits to 0. The end result has only that one rightmost 1-bit set. For example, 01010100 (rightmost bit in bold) gets turned into 00000100.
bitset & -bitset
5
6
請補完上述程式碼,使得功能與 GNU extension: __alignof__ 等價。
將表示式拆解
接著往外層看
最後
延伸問題
延伸問題:
ALIGN
, ALIGN_DOWN
, ALIGN_UP
等巨集,探討其實作機制和用途,並舉例探討 (可和上述第二點的案例重複)。思索能否對 Linux 核心提交貢獻,儘量共用相同的巨集7
先從以下程式碼段落推敲出判斷整除的核心邏輯
利用到編碼循環的特性 (但不是很好理解 :(
簡單來說 3 如果可以整除 i,is_divisible(i, M3)
會回傳 true
再來看主要功能,
Condition | length | (8>>div5)>>(KK3) |
---|---|---|
3 的倍數 (非 5 的倍數) | 4 | 0 |
5 的倍數 (非 3 的倍數) | 4 | 4 |
15 的倍數 | 8 | 0 |
其他 | digit length | 8 |
(2 << div3) << div5
(8 >> div5) >> (div3 << 2)
延伸問題:
naive.c
和 bitwise.c
效能落差printf
更換為 sprintf
bitwise.c
程式碼,試圖運用更少的指令來實作出 branchless;kernel/time/
目錄的標頭檔和程式碼)
過程中,你可能會發現可貢獻到 Linux 核心的空間,請充分討論