# 2022q1 Homework3 (quiz3)
contributed by <```DokiDokiPB```>
> [2022q1 第 3 週測驗題](https://hackmd.io/@sysprog/linux2022-quiz3)
### 測驗 ```1```
```c
#define GENMASK(h, l) \
(((~0UL) >> (LEFT)) & ((~0UL) >> (l) << (RIGHT)))
```
取 ```LEFT``` 為 ```63 - h``` (題目假設為 64 位元,取 64 - 1 = 63)
取 ```RIGHT``` 為 ```l```
利用在測驗題敘述中的例子作說明,有 8 位元:```GENMASK(6, 4)``` 產生 $0111 \space 0000_2$
1. ```~0UL``` 產生 $1111 \space 1111_2$
2. ```(~0UL) >> (8 - 6 + 1)``` 產生 $0111 \space 1111_2$
3. ```(~0UL) >> (l)``` 產生 $0000 \space 1111_2$
4. ``` ... << (RIGHT)``` 產生 $1111 \space 0000_2$
5. 最後取 ```&```
第 2 步驟中,目的是產生最高位有 ```7 - h``` 個零,而第 4 步產生最低位有 ```l``` 個零。
#### 延伸問題:比較 Linux 核心 GENMASK 巨集的實作,闡述其額外的考量
> - 延伸問題的內容跟如何閱讀是參考自 [hsuedw 的筆記](https://hackmd.io/@hsuedw/linux2022-quiz3#%E5%BB%B6%E4%BC%B8%E5%95%8F%E9%A1%8C-2-%E6%AF%94%E8%BC%83-Linux-%E6%A0%B8%E5%BF%83-GENMASK-%EF%BC%8C%E9%97%A1%E8%BF%B0%E5%85%B6%E9%A1%8D%E5%A4%96%E7%9A%84%E8%80%83%E9%87%8F)
> - [The Linux Kernel Archives](https://www.kernel.org/) 下載 Linux 原始程式碼,以下程式碼使用 Linux-5.17.1 版本
```GENMASK``` 實作於 [include/linux/bits.h](https://github.com/torvalds/linux/blob/master/include/linux/bits.h)
```c
#if !defined(__ASSEMBLY__)
#include <linux/build_bug.h>
#define GENMASK_INPUT_CHECK(h, l) \
(BUILD_BUG_ON_ZERO(__builtin_choose_expr( \
__is_constexpr((l) > (h)), (l) > (h), 0)))
#else
#define GENMASK_INPUT_CHECK(h, l) 0
#endif
#define __GENMASK(h, l) \
(((~UL(0)) - (UL(1) << (l)) + 1) & \
(~UL(0) >> (BITS_PER_LONG - 1 - (h))))
#define GENMASK(h, l) \
(GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))
```
與測驗題中的差異在於,使用 ```GENMASK_INPUT_CHECK(h, l)``` 在編譯時期檢查是否輸入錯誤的參數,例如 ```GENMASK(4, 6)```。
##### ```BUILD_BUG_ON_ZERO```
實作於 ```include/linux/build_bug.h```
```c
/*
* Force a compilation error if condition is true, but also produce a
* result (of value 0 and type int), so the expression can be used
* e.g. in a structure initializer (or where-ever else comma expressions
* aren't permitted).
*/
#define BUILD_BUG_ON_ZERO(e) ((int)(sizeof(struct { int:(-!!(e)); })))
```
根據註釋,若參數輸入 ```e``` 為 ```0```,巨集將會擴展為 ```struct { int: 0; }```,屬於合法的程式碼。 但若輸入的數字為非零,將會被擴展為 ```struct { int : -1; }```,無法通過編譯。
##### ```__builtin_choose_expr(const_exp, exp1, exp2)```
為 GCC 的 built-in function
> You can use the built-in function *builtinchooseexpr* to evaluate code depending on the value of a constant expression.
> This built-in function returns *exp1* if *constexp*, which is an integer constant expression, is nonzero. Otherwise it returns *exp2*
> This built-in function is analogous to the ‘? :’ operator in C, except that the expression returned has its type unaltered by promotion rules. Also, the built-in function does not evaluate the expression that is not chosen. For example, if const_exp evaluates to true, exp2 is not evaluated even if it has side effects.
> 來源:[Other Built-in Functions Provided by GCC](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)
根據註釋,是根據 *const_exp* 決定輸出哪個表示式,效果類似於 C language 的 "? :" 運算子。
##### ```__is_constexpr```
位於 ```include/linux/const.h``` 的巨集。
```c
/*
* This returns a constant expression while determining if an argument is
* a constant expression, most importantly without evaluating the argument.
* Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de>
*/
#define __is_constexpr(x) \
(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
```
根據註釋,用於判斷輸入的參數是否為 constant expression,在執行階段前被計算出來的表示式。
> Constant Expression:
> A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.
>
> 來源 C99 Standard
#### 延伸問題:舉出 Linux 核心原始程式碼中二處 GENMASK 巨集和 include/linux/bitfield.h 的應用案例
:::warning
這裡觀察局部的程式碼的實作,但是尚未有想法能夠解說此應用案例
:::
透過搜尋字串 ```include <linux/bitfield.h>``` 查詢到檔案 [```arch/arm64/kvm/hyp/pgtable.c``` ](https://github.com/torvalds/linux/blob/master/arch/arm64/kvm/hyp/pgtable.c) ,擷取部分的程式碼作為延伸問題的項目。
```pgtable.c``` 使用 ```bitfield.h``` 中的 ```FIELD_PREP```,以下列出部分的程式碼實作。
##### ```include/linux/bitfield.h```
```c
#define __bf_shf(x) (__builtin_ffsll(x) - 1)
/**
* FIELD_PREP() - prepare a bitfield element
* @_mask: shifted mask defining the field's length and position
* @_val: value to put in the field
*
* FIELD_PREP() masks and shifts up the value. The result should
* be combined with other fields of the bitfield using logical OR.
*/
#define FIELD_PREP(_mask, _val) \
({ \
__BF_FIELD_CHECK(_mask, 0ULL, _val, "FIELD_PREP: "); \
((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
})
```
```__builtin_ffsll(x)``` 與 ```int __builtin_ffs (int x)``` 類似,差異在於使用 ```long long```
> Returns one plus the index of the least significant 1-bit of x, or if x is zero, returns zero.
> 來源:[Other Built-in Functions Provided by GCC](https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html)
```FIELD_PREP(_mask, _val)``` 將輸入的數值與 ```_mask``` 對齊,擁有一樣的 ```ffs``` 數值。
##### [```arch/arm64/kvm/hyp/pgtable.c``` ](https://github.com/torvalds/linux/blob/master/arch/arm64/kvm/hyp/pgtable.c)
```c=
#define KVM_PTE_LEAF_ATTR_LO_S1_ATTRIDX GENMASK(4, 2)
#define KVM_PTE_LEAF_ATTR_LO_S1_AP GENMASK(7, 6)
#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RO 3
#define KVM_PTE_LEAF_ATTR_LO_S1_AP_RW 1
#define KVM_PTE_LEAF_ATTR_LO_S1_SH GENMASK(9, 8)
#define KVM_PTE_LEAF_ATTR_LO_S1_SH_IS 3
#define KVM_PTE_LEAF_ATTR_LO_S1_AF BIT(10)
...
enum kvm_pgtable_prot {
KVM_PGTABLE_PROT_X = BIT(0),
KVM_PGTABLE_PROT_W = BIT(1),
KVM_PGTABLE_PROT_R = BIT(2),
KVM_PGTABLE_PROT_DEVICE = BIT(3),
KVM_PGTABLE_PROT_SW0 = BIT(55),
KVM_PGTABLE_PROT_SW1 = BIT(56),
KVM_PGTABLE_PROT_SW2 = BIT(57),
KVM_PGTABLE_PROT_SW3 = BIT(58),
};
...
static int hyp_set_prot_attr(enum kvm_pgtable_prot prot, kvm_pte_t *ptep)
{
bool device = prot & KVM_PGTABLE_PROT_DEVICE;
u32 mtype = device ? MT_DEVICE_nGnRE : MT_NORMAL;
kvm_pte_t attr = FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_ATTRIDX, mtype);
u32 sh = KVM_PTE_LEAF_ATTR_LO_S1_SH_IS;
u32 ap = (prot & KVM_PGTABLE_PROT_W) ? KVM_PTE_LEAF_ATTR_LO_S1_AP_RW :
KVM_PTE_LEAF_ATTR_LO_S1_AP_RO;
if (!(prot & KVM_PGTABLE_PROT_R))
return -EINVAL;
if (prot & KVM_PGTABLE_PROT_X) {
if (prot & KVM_PGTABLE_PROT_W)
return -EINVAL;
if (device)
return -EINVAL;
} else {
attr |= KVM_PTE_LEAF_ATTR_HI_S1_XN;
}
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_AP, ap);
attr |= FIELD_PREP(KVM_PTE_LEAF_ATTR_LO_S1_SH, sh);
attr |= KVM_PTE_LEAF_ATTR_LO_S1_AF;
attr |= prot & KVM_PTE_LEAF_ATTR_HI_SW;
*ptep = attr;
return 0;
}
```
在第 48 至 51 行中,使用```FIELD_PREP```, ```KVM_PTE_LEAF_ATTR_LO_S1_AP``` = ```GENMASK(7, 6)``` ,設定數值。
---
### 測驗 ```2```
```c
struct foo;
struct fd {
struct foo *foo;
unsigned int flags;
};
enum {
FOO_DEFAULT = 0,
FOO_ACTION,
FOO_UNLOCK,
} FOO_FLAGS;
static inline struct fd to_fd(unsigned long v)
{
return (struct fd){EXP1, v & 3};
}
```
取```EXP3``` 為 ```v & ~3UL```
其中 ```~3UL``` 表示 0xFFFFFFFC,```v & ~3UL``` 將最低的兩個位元設為零
位址 ```unsigned long v``` 可能為某個 2 的冪,並且透過 ```v & 3``` 可以得知最低的兩個位元用於紀錄 FOO_FLAGS。最終可以推導出 ```unsigned long v``` 為 4 的倍數
> data 的 address 可以公平的被 1, 2, 4, 8,這些數字整除,從這些數字可以發現他們都是 2 的 N 次方 (N為從 0 開始的整數)。換句話說,這些 data object 可以用 1, 2, 4, 8 byte 去 alignment
> 節錄自〈[你所不知道的 C 語言:記憶體管理、對齊及硬體特性 data alignment](https://hackmd.io/@sysprog/c-memory#data-alignment)〉
#### 延伸問題:在 Linux 核心原始程式碼找出類似技巧的實作 (例如檔案系統) 並解說
> [The Linux Kernel Archives](https://www.kernel.org/) 下載 Linux 原始程式碼,以下程式碼使用 Linux-5.17.1 版本
在 Linux Source Code 搜尋 ```align_down``` ,檔案範圍限定在 fs 資料夾,可以得到 ```fs/btrfs/inode.c```
> Btrfs
> Btrfs is a computer storage format that combines a file system based on the copy-on-write (COW) principle with a logical volume manager.
> 來源: [Wikipedia Btrfs](https://en.wikipedia.org/wiki/Btrfs)
---
### 測驗 ```3```
```c
#include <stdint.h>
uint8_t rev8(uint8_t x)
{
x = (x >> 4) | (x << 4);
x = ((x & 0xCC) >> 2) | (EXP2);
x = ((x & 0xAA) >> 1) | (EXP3);
return x;
}
```
取 ```EXP2``` 為 ```(x & 0x33) << 2``` ($0x33 = 0b00110011$)
取 ```EXP3``` 為 ```(x & 0x55) << 1``` ($0x55 = 0b01010101$)
透過一個簡單的例子解釋:
例如 $ABCD \rightarrow DCBA$
1. AB 與 CD 對調位置,獲得 $CD \space AB$
2. C 與 D 對調位置,獲得 $DC$;B 與 A 對調位置,獲得 $BA$;
3. 最終獲得 $DCBA$
#### 延伸問題
ethernet 相關可以找到程式碼
---
### 測驗 ```4```
```c
#include <assert.h>
#define _foreach_no_nullval(i, p, arr) \
assert((i) >= sizeof(arr) / sizeof(arr[0]) || (p))
#define foreach_int(i, ...) \
for (unsigned _foreach_i = (((i) = ((int[]){__VA_ARGS__})[0]), 0); \
_foreach_i < sizeof((int[]){__VA_ARGS__}) / sizeof(int); \
(i) = ((int[]){__VA_ARGS__, 0})[EXP4])
#define foreach_ptr(i, ...) \
for (unsigned _foreach_i = \
(((i) = (void *) ((typeof(i)[]){__VA_ARGS__})[0]), 0); \
(i); (i) = (void *) ((typeof(i)[]){__VA_ARGS__, \
NULL})[EXP5], \
_foreach_no_nullval(_foreach_i, i, \
((const void *[]){__VA_ARGS__})))
```
取 ```EXP4``` 為 ```++_foreach_i```
取 ```EXP5``` 為 ```++_foreach_i```
##### 第一個巨集 ```#define foreach_int(i, ...)```
透過 vscode 編輯器提供的巨集擴展,例如以下程式碼:
```c
foreach_int(i, 1, 2, 3, 4, 5, 6) { printf("%d ", i); }
```
擴展為:
```c=
for (unsigned _foreach_i = (((i) = ((int[]){1, 2, 3, 4, 5, 6})[0]), 0);
_foreach_i < sizeof((int[]){1, 2, 3, 4, 5, 6}) / sizeof(int);
(i) = ((int[]){1, 2, 3, 4, 5, 6, 0})[++_foreach_i]) {
printf("%d ", i);
}
```
```_foreach_i``` 在此 macro 中,數值會依序為 ```0``` 至 ```5```,```i``` 則為 ```1``` 至 ```5```
##### 第二個巨集 ```#define foreach_int(i, ...)```
透過 vscode 編輯器提供的巨集擴展,例如以下程式碼:
```c
char *pp;
foreach_ptr(pp, "Hello", "World"){}
```
擴展為:
```c
for (unsigned _foreach_i = (((pp) = (void *)((typeof(pp)[]){"Hello", "World"})[0]), 0);
(pp);
(pp) = (void *)((typeof(pp)[]){"Hello", "World", NULL})[++_foreach_i], ...) {
}
```
```pp``` 的數值依序為```{"Hello", "World", NULL}```,當 ```pp``` 為 ```NULL```,即離開這個迴圈
#### 延伸問題
stdarg
---
### 測驗 ```5```
[LeetCode 29. Divide Two Integers](https://leetcode.com/problems/divide-two-integers/)
```c=
#include <limits.h>
int divide(int dividend, int divisor)
{
int signal = 1;
unsigned int dvd = dividend;
if (dividend < 0) {
signal *= -1;
dvd = ~dvd + 1;
}
unsigned int dvs = divisor;
if (divisor < 0) {
signal *= -1;
dvs = ~dvs + 1;
}
int shift = 0;
while (dvd > (EXP6))
shift++;
unsigned int res = 0;
while (dvd >= dvs) {
while (dvd < (dvs << shift))
shift--;
res |= (unsigned int) 1 << shift;
EXP7;
}
if (signal == 1 && res >= INT_MAX)
return INT_MAX;
return res * signal;
}
```
取 ```EXP6``` 為 ```dvs << shift```
取 ```EXP7``` 為 ```dvd -= dvs << shift```
根據 [LeetCode 29. Divide Two Integers](https://leetcode.com/problems/divide-two-integers/),輸入 8.325 會捨入至 8、輸入 -2.735 會捨入至 -2,屬於無條件捨去小數點。1並且輸出結果為商數,需要包含正負號
在此測驗題的程式碼中,第 4 ~ 15 行處理正負問題,並在最後輸出結果時處理。
第 17 ~ 27 中,根據直除法計算方式,先從高位數字開始算起,往低位減去除數。
示範以上程式碼過程:
```
dividend = 97
divisor = 2
97 - 64 = 33 // divisor * 2^5
33 - 32 = 1 // divisor * 2^4
1 < 2 // break loop
輸出商數: 48 = 0b00110000
```
<!-- #### 延伸問題:在 Linux 核心原始程式碼中找出針對整數/浮點數/定點數除法特別撰寫的程式碼,並予以討論 -->
<!--
### 測驗 ```6```
2:16:14
spare data structure
memory Linux compaction LWN.net
-->
---
### 測驗 ```7```
考慮 ```ilog32``` 函式可針對給定的 32 位元無號數,計算扣除開頭的``` 0```,最少需要多少位元才可儲存 (the minimum number of bits required to store an unsigned 32-bit value without any leading zero bits),以下是一個可能的實作:
```c=
int ilog32(uint32_t v)
{
int ret = v > 0;
int m = (v > 0xFFFFU) << 4;
v >>= m;
ret |= m;
m = (v > 0xFFU) << 3;
v >>= m;
ret |= m;
m = (v > 0xFU) << 2;
v >>= m;
ret |= m;
m = EXP10;
v >>= m;
ret |= m;
EXP11;
return ret;
}
```
取 ```EXP10``` 為 ```(v > 0x3) << 1```
取 ```EXP11``` 為 ```ret += (v >> 1);```
這測驗題可以將 32 減去 ```__builtin_clz```,即可得到相同輸出。
```v``` 為 ```uint_32_t```,因此有 32 bits,每次步驟皆為檢查左半部:
- 在第 3、4、5 行中,檢查 ```v[31:16]```:
如果 ```v > 0xFFFFU```,表示在 ```v[31:16]``` 至少有一位元為 1。因此此時 ```ret |= m``` 上記錄 16,表示需要 16 個 bits。
- 在第 6、7、8 行中,檢查 ```v[31:24]```:因為在第 5 行中已經位移,因此計算變成 ```v > 0xFFU```
- 同理檢查 ```v[31:28]```、```v[31:30]```
### 測驗 ```8```
考慮以下 C++ 二元搜尋樹的程式:
```c
typedef struct tnode *tree;
struct tnode {
int data;
tnode *left;
tnode *right;
tnode(int d)
{
data = d;
left = right = 0;
}
};
void remove_data(tree &t, int d)
{
tnode *p = t;
tnode *q;
while (p != 0 && p->data != d) {
if (d < p->data) {
q = p;
p = p->left;
} else {
q = p;
p = p->right;
}
}
if (p != 0) {
if (p->left == 0)
if (p == t)
t = p->right;
else if (p == q->left)
q->left = p->right;
else
q->right = p->right;
else if (p->right == 0)
if (p == t)
t = p->left;
else if (p == q->left)
q->left = p->left;
else
q->right = p->left;
else {
tnode *r = p->right;
while (r->left) {
q = r;
r = r->left;
}
p->data = r->data;
if (r == p->right)
p->right = r->right;
else
q->left = r->right;
p = r;
}
delete p;
}
}
```
函式 ```remove_data``` 嘗試將指定的資料,自給定的二元搜尋樹移除,但原本的 remove_data 過於冗長,於是我們可善用 indirect pointer 改寫為以下:
```c
void remove_data(tree &t, int d)
{
tnode **p = &t;
while (*p != 0 && (*p)->data != d) {
if (d < (*p)->data)
EXP12;
else
EXP13;
}
tnode *q = *p;
if (!q)
return;
if (!q->left)
*p = q->right;
else if (!q->right)
*p = q->left;
else {
tnode **r = &q->right;
while ((*r)->left)
r = EXP14;
q->data = (*r)->data;
q = *r;
*r = q->right;
}
delete q;
}
```
取 ```EXP12``` 為 ...
取 ```EXP13``` 為 ...
取 ```EXP14``` 為 ...
#### 延伸問題:以 C99 改寫上述程式碼,並寫出更精簡高效的實作,和最初的實作相比,探討效率的提升
#### 延伸問題:研讀 [Static B-Trees](https://en.algorithmica.org/hpc/data-structures/s-tree/),探討針對記憶體佈局 (memory layout) 的改進策略
---
### 測驗 ```9```
考慮以下進行記憶體地址對齊 (alignment) 的巨集:
```c
/* maximum alignment needed for any type on this platform, rounded up to a
power of two */
#define MAX_ALIGNMENT 16
/* Given a size, round up to the next multiple of sizeof(void *) */
#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \
(((x) + MAX_ALIGNMENT - MMM) & ~(NNN))
```
取 ```MMM``` 為 1
取 ```NNN``` 為 ```MAX_ALIGNMENT - 1```
> 答案參考自 [Github gist zerojiu/MacroMemory.cxx](https://gist.github.com/zerojiu/5673c6c1cbb21408100381b6cfa07fc5)
數學式為:$\lceil x/align \rceil * align$:
若 ```x``` 為 ```0```,則對齊後的結果為 ```0```
若 ```x``` 為 ```1 ~ 16```,則對齊後的結果為 ```16```
若 ```x``` 為 ```17 ~ 32```,則對齊後的結果為 ```32```
##### 寫出 ```ROUND_DOWN``` 巨集
數學式為 $\lfloor x/align \rfloor * align$
若 ```x``` 為 ```0 ~ 15```,則對齊後的結果為 ```0```
若 ```x``` 為 ```16 ~ 31```,則對齊後的結果為 ```16```
若 ```x``` 為 ```32 ~ 47```,則對齊後的結果為 ```32```
無條件捨去 ```0xF``` 的位元數,因此取
```c
#define MAX_ALIGNMENT 16
#define ROUND_UP_TO_ALIGNMENT_SIZE(x) \
((x) & ~(MAX_ALIGNMENT - 1))
```
#### 延伸問題:在 Linux 核心找出類似的巨集和程式碼,說明 round-up/down 的應用場合
在 [/include/linux/align.h](https://github.com/torvalds/linux/blob/a48b0872e69428d3d02994dcfad3519f01def7fa/include/linux/align.h#L8) 具類似的程式碼
```c
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a))
#define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask))
```
來自 /includeu/api/linux/const.h
```c
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
```
:::warning
閱讀 Windows APT Warfare 的 PE Header 之中也有對齊的程式碼,我想 ELF format 有相關的程式碼可以參考
:::
---
### 測驗 ```10```
考慮以下巨集可得到二個表示式進行整數除法操作時,最接近的整數值:
其中 ```divisor``` 預期為正數,負數行為沒有定義。
```c
#define DIVIDE_ROUND_CLOSEST(x, divisor) \
({ \
typeof(x) __x = x; \
typeof(divisor) __d = divisor; \
(((typeof(x)) -1) > 0 || ((typeof(divisor)) -1) > 0 || \
(((__x) > 0) == ((__d) > 0))) \
? ((RRR) / (__d)) \
: ((SSS) / (__d)); \
})
```
取 ```RRR``` 為 ```((__x) + ((__d) >> 1))```
取 ```SSS``` 為 ```((__x) - ((__d) >> 1))```
在一般數學計算中, $\lfloor x / divisor \rfloor$ 屬於無條件捨去小數部分,而 $\lfloor \frac{x + divisor/2 }{divisor} \rfloor$ 屬於四捨五入。
##### 參考的執行結果,```x```、```divisor```為正數:
DIVIDE_ROUND_CLOSEST(x, divisor),取 ```x``` 為 ```0 ~ 16```, ```divisor``` 為 ```3```
```
DIVIDE_ROUND_CLOSEST(x, divisor)
00 0
01 0
02 1
03 1
04 1
05 2
06 2
07 2
08 3
09 3
10 3
11 4
12 4
13 4
14 5
15 5
16 5
```
測驗題的要求為,被除數捨入至最近的整數數字,在上述只考慮正數的情況中
以 ``` 7 / 3 = 2.3333... ``` 為例,因為 ``` 2.333 < 2.5```,因此捨去 0.333, ```DIVIDE_ROUND_CLOSEST(7, 3)``` 的輸出為 ```2```
以 ``` 8 / 3 = 2.6666... ``` 為例,因為 ``` 2.666 > 2.5```,因此進位,```DIVIDE_ROUND_CLOSEST(8, 3)``` 的輸出為 ```3```
因此取 ```RRR``` 為 ```((__x) + ((__d) >> 1))```
##### 參考的執行結果,```x```為負數、```divisor```為負正數:
DIVIDE_ROUND_CLOSEST(x, divisor),取 ```x``` 為 ```0 ~ 16```, ```divisor``` 為 ```3```
```
00 0
-01 0
-02 -1
-03 -1
-04 -1
-05 -2
-06 -2
-07 -2
-08 -3
-09 -3
-10 -3
-11 -4
-12 -4
-13 -4
-14 -5
-15 -5
-16 -5
```
以 ``` -7 / 3 = -2.3333... ``` 為例,因為 ``` -2.333 > -2.5```,因此捨去 0.333, ```DIVIDE_ROUND_CLOSEST(7, 3)``` 的輸出為 ```2```
以 ``` -8 / 3 = 2.6666... ``` 為例,因為 ``` -2.666 < -2.5```,因此進位,```DIVIDE_ROUND_CLOSEST(8, 3)``` 的輸出為 ```3```
因此取 ```SSS``` 為 ```((__x) - ((__d) >> 1))```
<!--
### 測驗 ```11```
考慮以下計算整數開平方根的實作:
```c
static inline unsigned long fls(unsigned long word)
{
int num = 64 - 1;
if (!(word & (~0ul << 32))) {
num -= 32;
word <<= 32;
}
if (!(word & (~0ul << (64 - 16)))) {
num -= 16;
word <<= 16;
}
if (!(word & (~0ul << (64 - 8)))) {
num -= 8;
word <<= 8;
}
if (!(word & (~0ul << (64 - 4)))) {
num -= 4;
word <<= 4;
}
if (!(word & (~0ul << (64 - 2)))) {
num -= 2;
word <<= 2;
}
if (!(word & (~0ul << (64 - 1))))
num -= 1;
return num;
}
unsigned long i_sqrt(unsigned long x)
{
unsigned long b, m, y = 0;
if (x <= 1)
return x;
m = 1UL << (fls(x) & ~1UL);
while (m) {
b = y + m;
XXX;
if (x >= b) {
YYY;
y += m;
}
ZZZ;
}
return y;
}
```
```i_sqrt``` 函式的作用等同於 ```floor(sqrt(x))``` ($\lfloor sqrt(x) \rfloor$)
-->