# 2020q3 Homework6 (quiz6)
contributed by < `ptzling310` >
>[2020q3 第 6 週測驗題](https://hackmd.io/@sysprog/2020-quiz6)
---
## 測驗 `1`: bfloat16
```c=
float fp32tobf16(float x) {
float y = x;
int *py = (int *) &y;
unsigned int exp, man;
exp = *py & 0x7F800000u;
man = *py & 0x007FFFFFu;
if (!exp && !man) /* zero */
return x;
if (exp == 0x7F800000u) /* infinity or NaN */
return x;
/* Normalized number. round to nearest */
float r = x;
int *pr = (int *) &r;
*pr &= BB1;
r /= 256;
y = x + r;
*py &= BB2;
return y;
}
```
### 程式
```c=5
exp = *py & 0x7F800000u;
man = *py & 0x007FFFFFu;
```
`exp` 是取出 `y` 的 Exponent(E) 的部分,共 8 bits。
`man` 是取出 `y` 的 Fraction(F) 的部分,共 23 bits。
```c=7
if (!exp && !man) /* zero */
return x;
```
如果 `exp` 以及 `man` 皆為 0 表示該 float point 為 `±0`。
```c=9
if (exp == 0x7F800000u) /* infinity or NaN */
return x;
```
```graphviz
digraph structs {
node[shape=record]
NaN[label="NaN",shape=plaintext];
struct1 [label="0/1|11111111|nonZero"];
{rank= NaN; struct1}
INF[label="INF",shape=plaintext];
struct2 [label="0/1 | 11111111| 0..0"];
{rank= INF; struct2}
}
```
若 Exponent(E) 的部分皆為 1 ,則為 NaN 或 INF 的情況,故 `retrun x`即可。
```c=12
/* Normalized number. round to nearest */
float r = x;
int *pr = (int *) &r;
*pr &= 0xff800000;
r /= 256;
y = x + r;
```
目前:`*pr`=`r` = `x` ; `*py` = `y` = `x`。
此段進行進位處理。
假設目前一 float point : ($b_{31}|b_{30}b_{29}b_{28}...b_{23}|b_{22}...b_{16}b_{15}...b_0$)~2~,其中:$b_{31}$ 是 Sign(S);$b_{30}-b_{23}$ 是 Exponent(E) 的部分;$b_{22}-b_{16}$ 共 7 bits 應為要保留的小數點,再來要經由$b_{15}$ 來判斷是否該進位。
在 line `17` 中,`x+r` 後會使進位達成,也就是說若 `x` 的 $b_{15}$ 為 bit 1,則會進位。故 `r` 的 Fraction(F) 應該為 (000000010...0)~2~。
`x` = $(-1)^{S}\times1.fraction\times2^E$,`r`=$(-1)^{S}\times1\times2^E$。
於 line `16`:`\256` = $÷2^8$,故 `r/256` = $(-1)^{S}\times2^E÷2^8=(-1)^{S}\times2^{E-8}$。
於 line `17`:`y=x+r` :
$\begin{aligned}x+r&=(-1)^{S}\times1.fraction\times2^E+(-1)^{S}\times2^{E-8}\\
&=(-1)^{S}\times2^{E}\times(1.fraction+2^{-8}) \\
&=(-1)^{S}\times2^{E}\times(1.fraciton+0.000000001)\end{aligned}$
```c=
*py &= BB2;
```
由於最終只保留$b_{31}-b_{16}$的部分,所以$b_{15}-b_0$ (共 16 bits) 清除為0。故 BB2 =`0xffff0000`。
-----------
## 測驗 `2`: Ring buffer
```c=
#define RINGBUF_DECL(T, NAME) \
typedef struct { \
int size; \
int start, end; \
T *elements; \
} NAME
#define RINGBUF_INIT(BUF, S, T) \
{ \
static T static_ringbuf_mem[S + 1]; \
BUF.elements = static_ringbuf_mem; \
} \
BUF.size = S; \
BUF.start = 0; \
BUF.end = 0;
#define NEXT_START_INDEX(BUF) \
(((BUF)->start != (BUF)->size) ? ((BUF)->start + RB1) : 0)
#define NEXT_END_INDEX(BUF) (((BUF)->end != (BUF)->size) ? ((BUF)->end + RB2) : 0)
#define is_ringbuf_empty(BUF) ((BUF)->end == (BUF)->start)
#define is_ringbuf_full(BUF) (NEXT_END_INDEX(BUF) == (BUF)->start)
#define ringbuf_write_peek(BUF) (BUF)->elements[(BUF)->end]
#define ringbuf_write_skip(BUF) \
do { \
(BUF)->end = NEXT_END_INDEX(BUF); \
if (is_ringbuf_empty(BUF)) \
(BUF)->start = NEXT_START_INDEX(BUF); \
} while (0)
#define ringbuf_read_peek(BUF) (BUF)->elements[(BUF)->start]
#define ringbuf_read_skip(BUF) (BUF)->start = NEXT_START_INDEX(BUF);
#define ringbuf_write(BUF, ELEMENT) \
do { \
ringbuf_write_peek(BUF) = ELEMENT; \
ringbuf_write_skip(BUF); \
} while (0)
#define ringbuf_read(BUF, ELEMENT) \
do { \
ELEMENT = ringbuf_read_peek(BUF); \
ringbuf_read_skip(BUF); \
} while (0)
```
::: spoiler 測試程式碼
```c
#include <assert.h>
RINGBUF_DECL(int, int_buf);
int main()
{
int_buf my_buf;
RINGBUF_INIT(my_buf, 2, int);
assert(is_ringbuf_empty(&my_buf));
ringbuf_write(&my_buf, 37);
ringbuf_write(&my_buf, 72);
assert(!is_ringbuf_empty(&my_buf));
int first;
ringbuf_read(&my_buf, first);
assert(first == 37);
int second;
ringbuf_read(&my_buf, second);
assert(second == 72);
return 0;
}
```
:::
### Function
#### RINGBUF_DECL
```c
#define RINGBUF_DECL(T, NAME) \
typedef struct { \
int size; \
int start, end; \
T *elements; \
} NAME
```
Ring buffer 會有紀錄
- `size`:紀錄此 buffer 大小
- `start`:紀錄第一筆資料的位子。
- `end`:紀錄最後一筆資料的下一格位子。
- `elemnets`:型態為 T 的資料。
#### RINGBUF_INIT
```c=8
#define RINGBUF_INIT(BUF, S, T) \
{ \
static T static_ringbuf_mem[S + 1]; \
BUF.elements = static_ringbuf_mem; \
} \
BUF.size = S; \
BUF.start = 0; \
BUF.end = 0;
```
Parameters:
- `BUF` :這個 buffer 的變數名稱。
- `S` :這個 buffer 的大小。
- `T` :這個 buffer 所存的類型。
這裡是在建立一個新的 ring buffer,且大小為輸入的 `S+1`。
#### NEXT_START_INDEX
判斷接下來 `start` 的位子。
```c=17
#define NEXT_START_INDEX(BUF) \
(((BUF)->start != (BUF)->size) ? ((BUF)->start + RB1) : 0)
```
若 `start` 已在 buffer 的最後一格,則 `start` 則移動至第一格,已達 ring 的效果;若無,則將 `start` 往後一格。故 RB1 =`1`。
#### NEXT_END_INDEX(BUF)
判斷接下來 `end` 的位子。
```c=19
#define NEXT_END_INDEX(BUF) (((BUF)->end != (BUF)->size) ? ((BUF)->end + RB2) : 0)
```
若 `end` 未達最後一格(`size`),則可將 `end` 往後一格;若已達 `size`,則將 `end` 移到第一個位子,此舉即可達 ring 的效果。故 RB2 = `1`。
#### is_ringbuf_empty
```c=21
#define is_ringbuf_empty(BUF) ((BUF)->end == (BUF)->start)
```
若 `start` 與 `end` 指向同一格,則表示此 buffer 為 empty。
```graphviz
digraph structs
{
node[shape=record]
struct [label="<f0>||||<f1>"];
start[label="start",shape=plaintext];
end[label="end",shape=plaintext];
start->struct:<f0>
end->struct:<f0>
}
```
#### is_ringbuf_full
```c=22
#define is_ringbuf_full(BUF) (NEXT_END_INDEX(BUF) == (BUF)->start)
```
如果下一筆資料會被放在與 `start` 相同的位子,代表此 buffer 已滿。
```graphviz
digraph structs
{
node[shape=record]
struct [label="<f0>1|2|3|4|<f1>5"];
start[label="start",shape=plaintext];
end[label="end",shape=plaintext];
start->struct:<f0>
end->struct:<f1>
}
```
#### ringbuf_write_peek
```c=24
#define ringbuf_write_peek(BUF) (BUF)->elements[(BUF)->end]
```
會將東西填入 buff 的 `end` 處。
#### ringbuf_write_skip
```c=25
#define ringbuf_write_skip(BUF) \
do { \
(BUF)->end = NEXT_END_INDEX(BUF); \
if (is_ringbuf_empty(BUF)) \
(BUF)->start = NEXT_START_INDEX(BUF); \
} while (0)
```
在 line `27` 先找到下一個 `end` 所在的位子。
若此 buffer 是 empty ,表示接著要寫入的資料會為第一筆資料,所以 `start` 也要跟著移動到該筆資料的位子。
#### ringbuf_read_peek
```c=32
#define ringbuf_read_peek(BUF) (BUF)->elements[(BUF)->start]
```
取出 `start` 所指的位子的 elemetnt。
#### ringbuf_read_skip
```c=33
#define ringbuf_read_skip(BUF) (BUF)->start = NEXT_START_INDEX(BUF);
```
將 `start` 指到下一個位子。
#### ringbuf_write
```c=35
#define ringbuf_write(BUF, ELEMENT) \
do { \
ringbuf_write_peek(BUF) = ELEMENT; \
ringbuf_write_skip(BUF); \
} while (0)
```
Parameters:
- BUF:這個 buffer 的變數名稱。
- ELEMENT:所想要寫入的值。
藉由呼叫 `ringbuf_write_peek` 會將此值填 `end` 所指的位子。再由 `ringbuf_write_skip` 將 `end` 移動到下一個該指向的位子。
#### ringbuf_read
```c=41
#define ringbuf_read(BUF, ELEMENT) \
do { \
ELEMENT = ringbuf_read_peek(BUF); \
ringbuf_read_skip(BUF); \
} while (0)
```
呼叫 `ringbuf_read_peek` 讀取 element,再將 `start` 移動到正確的位子。
--------------------
## 測驗 `3`: Linked list
```c=1
#include <stdio.h>
/* clang-format off */
#define cons(x, y) (struct llist[]){{y, x}}
/* clang-format on */
struct llist {
int val;
struct llist *next;
};
void sorted_insert(struct llist **head, struct llist *node)
{
if (!*head || (*head)->val >= node->val) {
node->next = *head;
*head = node;
return;
}
struct llist *current = *head;
while (current->next && current->next->val < node->val)
current = current->next;
node->next = current->next;
current->next = node;
}
void sort(struct llist **head)
{
struct llist *sorted = NULL;
for (struct llist *current = *head; current;) {
struct llist *next = current->next;
sorted_insert(&sorted, current);
current = next;
}
*head = sorted;
}
int main()
{
struct llist *list = cons(cons(cons(cons(NULL, A), B), C), D);
struct llist *p;
for (p = list; p; p = p->next)
printf("%d", p->val);
printf("\n");
sort(&list);
for (p = list; p; p = p->next)
printf("%d", p->val);
printf("\n");
return 0;
}
```
### 程式
#### cons(x,y)
```c=4
/* clang-format off */
#define cons(x, y) (struct llist[]){{y, x}}
/* clang-format on */
struct llist {
int val;
struct llist *next;
};
```
此處是將 `x` 以及 `y` 連結,但這邊要注意是將 `y` 擺在 `x` 前方。
利用 compound literals 來將 `y`以及`x` 配給 `struct` 中的 `val` 以及 `next`。
即:
```c
.val = y;
.next = x;
```
所以在 line `39`:
```graphviz
digraph structs
{
rankdir=LR
node[shape=record]
A [label="A"];
B [label="B"];
NULL[label="NULL",shape=plaintext];
D->C->B->A->NULL
}
```
#### sort
```c=26
void sort(struct llist **head)
{
struct llist *sorted = NULL;
for (struct llist *current = *head; current;) {
struct llist *next = current->next;
sorted_insert(&sorted, current);
current = next;
}
*head = sorted;
}
```
將 link list 進行 insertion sort。
#### sorted_insert
```c=12
void sorted_insert(struct llist **head, struct llist *node)
{
if (!*head || (*head)->val >= node->val) {
SS1;
SS2;
return;
}
struct llist *current = *head;
while (current->next && current->next->val < node->val)
current = current->next;
node->next = current->next;
current->next = node;
}
```
`node` 是指本次要往前插入的 node,會經由 `while` 找到適合的位子並且將 node 插入。
若沒有 `head` 或者 `node` 會位在第一個位子時,便將 `node` 設為 `head`。
故 SS1 = `node->next = *head`; SS2 = `*head = node`。
--------------------
## 測驗 `4`: [LeetCode 287. Find the Duplicate Number](https://leetcode.com/problems/find-the-duplicate-number/)
值會在 **1 <= nums[i] <= n**。
```c=
int findDuplicate(int *nums, int numsSize)
{
int res = 0;
const size_t log_2 = 8 * sizeof(int) - __builtin_clz(numsSize);
for (size_t i = 0; i < log_2; i++) {
int bit = 1 << i;
int c1 = 0, c2 = 0;
for (size_t k = 0; k < numsSize; k++) {
if (k & bit)
++c1;
if (nums[k] & bit)
++c2;
}
if (CCC)
res += bit;
}
return res;
}
```
### 概念
舉例:1,3,4,2,2,`n`= 4。
所以出現的數字會有 1~4,將每個數字用二進制表示,再將 b~i~ 加總。
| number | b~2~ | b~1~ | b~0~
| -------- | -------- | -------- |-------- |
| 1 | 0 | 0 |1 |
| 2 | 0 | 1 |0 |
| 3 | 0 | 1 |1 |
| 4 | 1 | 0 |0 |
| sum | 1 | 2 | 2 |
如果今天重複的是 2=(010)~2~,則會發現 b~1~ 會多出 1。
### 程式
```c=4
const size_t log_2 = 8 * sizeof(int) - __builtin_clz(numsSize);
```
`log_2` 是用來判斷說我們所需計算幾個 bit 的加總。
```c=5
for (size_t i = 0; i < log_2; i++) {
int bit = 1 << i;
int c1 = 0, c2 = 0;
for (size_t k = 0; k < numsSize; k++) {
if (k & bit)
++c1;
if (nums[k] & bit)
++c2;
}
```
`k` 會是 1~n , 所以 `c1` 是用來紀 b~i~ 之 bit 1 個數;`c2` 就是紀錄在陣列中 b~i~ 之 bit 1 個數。
```c=14
if (CCC)
res += bit;
```
若 `c2` 比較大,代表 b~i~是有多出的數字,則將此 bit 記錄到 `res` 中。
最後 `res` 所存的值就會是重複的數字。
###### tags: `sysprog2020`