contributed by < Korin777
>
考慮以下對二個無號整數取平均值的程式碼:
這個直覺的解法會有 overflow 的問題,若我們已知 a, b 數值的大小,可用下方程式避免 overflow:
low
提出後 , 在對兩數差取平均(a >> 1) + (b >> 1)
相當於 2/a + 2/b
, 當兩數皆為奇數時多出來的餘數會使得 a/2 + b/2
比(a+b)/2
少1&1
)來達成a & b
會找出2進位表示中 , 兩數皆為1的位元a ^ b
會找出2進位表示中 , 只有一數為1的位元透過 objdump -d
來查看對應的組合語言輸出
rdi
: 儲存函式第一個 argument ,rsi
: 儲存函式第二個 argumenteax
: return value ,為 rax
的 Low 32-bits(%rdi,%rsi,1)
: rdi
裡的值與 1 倍的 rsi
裡的值的和 => a + blea (%rdi,%rsi,1),%eax
: 複製 (%rdi,%rsi,1)
的值到 %eax
=> %eax = a + bshr %eax
: 將 %eax
裡的值右移一位 => %eax = (a + b) / 2retq
: 結束 callee, 回到 caller 原本執行的地方edi
: 儲存函式第一個 argument ,esi
: 儲存函式第二個 argumentrdi
佔 64 bits,而 edi
則是 rdi
的 Low 32-bitsmov %esi,%eax
: 將 %esi
裡的值複製到 %eax
=> %eax = lowsub %edi,%eax
: 將 %eax
裡的值與 %edi
裡的值差存在 %eax
=> %eax = low - highshr %eax
: %eax = (low - high) / 2add %edi,%eax
: 將 %eax
裡的值與 %edi
裡的值和存在 %eax
=> %eax = low + (low - high) / 2%edx
: 儲存函式第三個 argument,因為函式只有兩個參數,這裡應該是用來暫存計算中的值mov %esi,%edx
: %edx = ashr %esi
: %esi = a >> 1and $0x1,%edx
: 將常數 0x1
與 %edx
裡的值做 & 運算並存在 %edx
=> %edx = a & 1and %edi,%edx
: %edx = a & 1 & bshr %edi
: %edi = b >> 1add %edx,%edi
: %edi = (b >> 1) + (a & b & 1)lea (%rdi,%rsi,1),%eax
: %eax = (a >> 1) + (b >> 1) + (a & b & 1)mov %edi,%eax
: %eax = aand %esi,%edi
: %edi = a & bxor %esi,%eax
: %eax = a ^ bshr %eax
: %eax = (a ^ b) >> 1lea (%rax,%rdi,1),%eax
: %eax = (a & b) + ((a ^ b) >> 1)參考資料
參考上面 32 位元無號整數實作,撰寫針對 32 位元有號並同樣 branchless 的實作
int32_t
u
為 0 回傳 v
, 反之回傳 u
, 兩者皆為 0 回傳 0u
和 v
皆為偶數 , , shift
用來記錄之後要乘回 2 多少次u
還是偶數 , 因為 v
是奇數 u
和 v
的最大公因數 , 在這過程中因為 u
一直保持為奇數 , 因此 shift
次方while(u | v)
應該改成 while(v)
,否則會陷入無窮迴圈,因為 v
才是過程中會減到 0 的數, 而 u
則是最大公因數透過 __builtin_ctz
改寫 GCD
bitmap
資料中 , 出現 1 的位置記錄在 out
, 並回傳 bitmap
出現 1 的次數 pos
bitmapsize
: 2bitmap
: bitmap[0] = 3'b101 , bitmap[1] = 3'b010out
: [0,2,4]pos
: 3bitset & -bitset
會保留 bitset
中 1 的最低位元 , 而將其他位元變成 0bitmap
資料都看過一遍 , 這裡每次就只會看位元為 1 的位置rem_node->key
儲存已經出現過的餘數,當在一次遇到同樣的餘數,表示小數循環了rem_node->index
儲存該餘數前有幾個數,這些數為小數沒循環的部分sprintf(p, "%ld", division > 0 ? (long) division : (long) -division);
應可改為 sprintf(p, "%ld", division);
,因為 division = n / d
又 n
、d
皆為正數type t
最小也只會跟 char
一樣佔 1 byte,所以 struct 一定會跟 type t
對齊type t
對齊幾個byte(char *) - 1
會減少1 byte,而 (float *) - 1
則會減少 4 byte,這也是為什麼要強制轉型成 (char *)
,因為我們不知道前面是什麼pointer,而當後面不是減 0 時,可能會產生上述的錯誤length
為 4 , 同時為 3 及 5 的倍數字串長度為 8,因此可得知 length = 2 << div3 << div5
KK3
應為 08 >> div5
,此時 KK3
應為 0div3
為 1,又 3 的倍數會使 div5
為 0,可知 8 >> KK3
應為 0,因此可知 KK3 >= 4
即 div3 << 2
strncpy(fmt, &"FizzBuzz%u"[(9 >> div5) >> (div3 << 2)], length);
中的 9 應改為 8,因 "%u" 開頭在字串 index 為 8 的位置