# 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)
:::
---