# 2018q1 第 5 週測驗題 (上) --- ### 測驗 `1` 考慮以下程式碼,推敲其作用 (選出最符合行為的描述) ```clike uint8_t Func32(uint32_t x) { return x ? Func32(x >> 1) - 1 : 32; } ``` 將 `Func32` 改寫為以下功能等價的程式碼: ```clike uint8_t FuncI(uint32_t x) { if (!x) return 32; int n = 1; if (!(x >> 16)) { n += 16; x <<= 16; } if (!(x >> M1)) { n += 8; x <<= 8; } if (!(x >> M2)) { n += 4; x <<= 4; } if (!(x >> M3)) { n += 2; x <<= 2; } n = n - (x >> 31); return n; } ``` ==作答區== Func32 = ? * `(a)` 計算對 `x` 的二進位表示法中,總有有幾個 bit 為 `1`,如果全部都是 `1`,那麼回傳 `32` * `(b)` 計算對 `x` 的二進位表示法中,總有有幾個 bit 為 `0`,如果全部都是 `0`,那麼回傳 `32` * `(c)` 計算輸入數值 `x` 的二進位表示中,回傳開頭 (從高位到低位元) 有幾個 0,倘若 `x` 為 `0x0`,則輸出 32 * `(d)` 計算輸入數值 `x` 的二進位表示中,回傳開頭 (從高位到低位元) 有幾個 1,倘若 `x` 每個 bit 均為 `1`,則輸出 32 * `(e)` 計算輸入數值 `x` 的二進位表示中,回傳尾巴 (低位元到高位) 有幾個 1,倘若 `x` 每個 bit 均為 `1`,則輸出 32 * `(f)` 計算輸入數值 `x` 的二進位表示中,回傳尾巴 (低位元到高位) 有幾個 0,倘若 `x` 為 `0x0`,則輸出 32 M1 = ? * `(a)` 0xF * `(b)` 0xFF * `(c)` 0x12 * `(d)` 0x14 * `(e)` 0x16 * `(f)` 0x18 * `(g)` 0x1A M2 = ? * `(a)` 0x1F * `(b)` 0x1E * `(c)` 0x1D * `(d)` 0x1C * `(e)` 0x1B * `(f)` 0x1A * `(g)` 0x10 M3 = ? * `(a)` 0x1E * `(b)` 0x1D * `(c)` 0x1C * `(d)` 0x1B * `(e)` 0x1A * `(f)` 0x10 :::success 延伸題目: 1. 在 x86_64/Aarch32/Aarch64 ISA 中找出對應於 `Func32` 的指令,隨後在 GitHub 專案中找到 3 個應用案例並解說; 2. 回顧 Week1 到 Week5 的教材和作業,提出可用對應於 `Func32` (或下方 `Func64`) 的指令加速的案例; ::: --- ### 測驗 `2` 延伸測驗 `1`,將 `Func32` 應用於以下程式碼,可輸入給定 64-bit 數值的 10 進位表達方式 (類似 `printf` 函式的 `%d` 格式),補完程式碼並推測其作用。 ```clike #include <stdint.h> #include <stdio.h> union u_qword { struct { uint32_t low; uint32_t high; } dwords; uint64_t qword; }; struct udiv_result { union u_qword q; union u_qword r; }; static inline int Func64(uint64_t val) { uint32_t lo = (uint32_t) val; uint32_t hi = (uint32_t) (val >> 32); return hi ? Func32(hi) : 32 + Func32(lo); } static int do_udiv32(uint32_t dividend, uint32_t divisor, struct udiv_result *res) { /* Ensure dividend is always greater than or equal to the divisor. */ uint32_t mask = Func32(divisor) - Func32(dividend); divisor <<= mask; /* align divisor */ mask = 1U << mask; /* align dividend */ do { if (dividend >= divisor) { dividend -= divisor; res->q.dwords.low |= mask; } divisor >>= 1; } while ((P1) && dividend); res->r.dwords.low = dividend; return 0; } int udiv64(uint64_t dividend, uint64_t divisor, struct udiv_result *res) { uint64_t mask; uint64_t bits; res->q.qword = res->r.qword = 0; if (divisor == 0) { /* division by 0 ? */ res->q.qword = 0xffffffffffffffffull; return -1; } if (divisor == dividend) { res->q.qword = 1; return 0; } if (divisor > dividend) { res->r.qword = dividend; return 0; } /* only 32 bit operands that the preconditions are fulfilled. */ if (!(divisor >> 32) && !(dividend >> 32)) return do_udiv32((uint32_t) dividend, (uint32_t) divisor, res); bits = Func64(divisor) - Func64(dividend); divisor <<= bits; /* align divisor and dividend */ mask = 1ULL << bits; /* division loop */ do { if (dividend >= divisor) { dividend -= divisor; res->q.qword |= mask; } divisor >>= 1; mask >>= 1; } while ((P2) && dividend); res->r.qword = dividend; return 0; } int main() { char digitbuff[20]; char *pos = digitbuff + sizeof(digitbuff); union u_qword v; /* current value */ union u_qword nv; /* next value */ struct udiv_result d; int64_t value = 0xCAFEBABE; /* Java classfile magic number */ v.qword = (unsigned long long) value; while (v.dwords.high != 0) { /* process 64 bit value as long as needed */ /* determine digits from right to left */ udiv64(v.qword, 10, &d); *--pos = d.r.dwords.low + P3; v.qword = d.q.qword; } do { /* process 32 bit (or reduced 64 bit) value */ nv.dwords.low = v.dwords.low / P4; *--pos = (v.dwords.low - (10 * nv.dwords.low)) + P5; } while ((v.dwords.low = nv.dwords.low) != 0); int len = digitbuff + sizeof(digitbuff) - pos; char *p = pos; while (len--) putchar(*p++); printf("\n"); return 0; } ``` ==作答區== `Func64` 的作用為? * `(a)` 計算對 `x` 的二進位表示法中,總有有幾個 bit 為 `1`,如果全部都是 `1`,那麼回傳 `64` * `(b)` 計算對 `x` 的二進位表示法中,總有有幾個 bit 為 `0`,如果全部都是 `0`,那麼回傳 `64` * `(c)` 計算輸入數值 `x` 的二進位表示中,回傳開頭 (從高位到低位元) 有幾個 0,倘若 `x` 為 `0x0`,則輸出 64 * `(d)` 計算輸入數值 `x` 的二進位表示中,回傳開頭 (從高位到低位元) 有幾個 1,倘若 `x` 每個 bit 均為 `1`,則輸出 64 * `(e)` 計算輸入數值 `x` 的二進位表示中,回傳尾巴 (低位元到高位) 有幾個 1,倘若 `x` 每個 bit 均為 `1`,則輸出 64 * `(f)` 計算輸入數值 `x` 的二進位表示中,回傳尾巴 (低位元到高位) 有幾個 0,倘若 `x` 為 `0x0`,則輸出 64 `P1` 應該為? * `(a)` divisor != 0 * `(b)` mask >>= 1 * `(c)` divisor & mask * `(d)` mask != 0 * `(e)` divisor | mask * `(f)` divisor ^ mask `P2` 應該為? * `(a)` divisor != 0 * `(b)` divisor & bits * `(c)` bits-- * `(d)` mask >>= 1 * `(e)` mask != 0 * `(f)` divisor | mask * `(g)` divisor ^ mask `P3` = ? * `(a)` 0x0 * `(b)` 0x1 * `(c)` 0x10 * `(d)` 0x11 * `(e)` 0x20 * `(f)` 0x21 * `(g)` 0x30 * `(h)` 0x31 `P4` = ? * `(a)` 2 * `(b)` 4 * `(c)` 8 * `(d)` 16 * `(e)` 10 * `(f)` 24 `P5` = ? * `(a)` 0x0 * `(b)` 0x1 * `(c)` 0x10 * `(d)` 0x11 * `(e)` 0x20 * `(f)` 0x21 * `(g)` 0x30 * `(h)` 0x31 :::success 延伸題目: 1. 解釋上述程式碼,從 newlib 或 glibc 原始程式碼中找出類似上方程式碼的實作 (提示: `sprintf` 函式),予以精簡並實作最小可執行的版本; 2. 將 10 進位輸出改為 16 進位 3. 實作浮點數輸出 4. 上方程式碼的 `0xCAFEBABE` 為 [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)),舉出在真實世界中程式的實際作用 (提示: 如 malloc) ::: ---
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.