# 2022q1 Homework1 (quiz2) contributed by < `a12345645` > ## 測驗 1 ```c #include <stdint.h> uint32_t average(uint32_t a, uint32_t b) { return (a >> 1) + (b >> 1) + (EXP1); } ``` 前面的 `(a >> 1) + (b >> 1)` 把 a 與 b 向右移動一個位元相加 可以視為 `floor(a / 2) + floor(b / 2)` 這樣與 `(a + b) / 2` 的差別會是最後一個位元的進位 因為最後一個位元被捨去了 所以 `EXP1` 為 `a & b & 1` 補回最又一個位元是否都為 1 的進位 ```c uint32_t average(uint32_t a, uint32_t b) { return (EXP2) + ((EXP3) >> 1); } ``` ![image alt](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/4-bit_ripple_carry_adder.svg/2560px-4-bit_ripple_carry_adder.svg.png) 這個是在模仿加法器 加法器的一個 bit 為 $$ S_{i} = (A_{i} or B_{i}) + (A_{i-1} and B_{i-1})$$ 所以會是 `S = (A ^ B) + ((A & B) << 1)` 然後 S 除以 2 就變成 `((A ^ B) >> 1) + (A & B)` `EXP2` = `a & b` `EXP3` = `a ^ b` ## 測驗 2 ```c #include <stdint.h> uint32_t max(uint32_t a, uint32_t b) { return a ^ ((EXP4) & -(EXP5)); } ``` 在 xor 運算中 `a ^ 0 = a` 與 `a ^ a = 0` 所以如果 a 比較大的的話就會希望是 `a ^ 0 = a` `((EXP4) & -(EXP5))` 等於 `0` 反之會希望為 `a ^ b` 所以 `(EXP4)` 為 `a ^ b` 而 `-(EXP5)` 為判斷 a, b 大小的遮罩 `EXP5` 是 `a < b` 當 a 小於 b 時,打開遮罩 大於的時候關起來 ## 測驗 3 ```c #include <stdint.h> uint64_t gcd64(uint64_t u, uint64_t v) { if (!u || !v) return u | v; int shift; for (shift = 0; !((u | v) & 1); shift++) { u /= 2, v /= 2; } while (!(u & 1)) u /= 2; do { while (!(v & 1)) v /= 2; if (u < v) { v -= u; } else { uint64_t t = u - v; u = v; v = t; } } while (COND); return RET; } ``` 1. 判斷 u 與 v 一方為 0 就回傳行一方 ```c if (!u || !v) return u | v; ``` 2. 如果 u 與 v 都是偶數,並同時除以 2 因為 `gcd(x, y) = 2 ∗ gcd(x / 2 ,y / 2)` 而除以 2 是可以使用 shift 做到 ```c int shift; for (shift = 0; !((u | v) & 1); shift++) { u /= 2, v /= 2; } ``` 3. 而如果 y 為奇數,而 `gcd(x, y) = gcd(x / 2 ,y)` ```c while (!(u & 1)) u /= 2; ``` 4. `COND` 為 `v > 0` `RET` 為 `u << shift` 前面的 `while` 就是在執行步驟 3 而下面的 `if else` 就是原本的輾轉箱除法只是使用減法實現 ```c do { while (!(v & 1)) v /= 2; if (u < v) { v -= u; } else { uint64_t t = u - v; u = v; v = t; } } while (v > 0); return u << shift; ``` ## 測驗 4 ```c #include <stddef.h> size_t improved(uint64_t *bitmap, size_t bitmapsize, uint32_t *out) { size_t pos = 0; uint64_t bitset; for (size_t k = 0; k < bitmapsize; ++k) { bitset = bitmap[k]; while (bitset != 0) { uint64_t t = EXP6; int r = __builtin_ctzll(bitset); out[pos++] = k * 64 + r; bitset ^= t; } } return pos; } ``` `EXP6` 的目的是要取得 bitset 最小位元為 1 的遮罩 並把他與 bitset 做 xor 清除 1 負數為取 NOT 並加 1 11001000 -> 00110111 -> 00111000 這樣再與原本的數 and 就會得到所要的遮罩 10001000 and 00111000 = 00001000 ## 測驗 5