# 2020q3 Homework6 (quiz6)
contributed by <`hsiehong`>
###### tags: `進階電腦系統理論與實作`
> [quiz6](https://hackmd.io/@sysprog/2020-quiz6)
> [其他繳交共筆](https://hackmd.io/@sysprog/2020-homework6)
### 測驗 1
FP32 to [BF16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format) 轉換程式
```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=
void print_hex(float x) {
int *p = (int *) &x;
printf("%f=%x\n", x, *p);
}
int main() {
float a[] = {3.140625, 1.2, 2.31, 3.46, 5.63};
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
print_hex(a[i]);
float bf_a = fp32tobf16(a[i]);
print_hex(bf_a);
}
return 0;
}
```
`BB1` = `0xff800000`
`BB2` = `0xffff0000`
![](https://i.imgur.com/JussPGa.png)
#### 程式碼運作機制
* 轉換前會先確認 floating point 是否是 `NaN`, `0`, 或 `infinity`,是的話就直接回傳,`exp` 跟 `man` 分別就是在讀取 `exponent part` 跟 `fraction part` 的資訊,由於 BF16 與 FP32 對於 `Nan`(exponent 全 1,fraction 全 0), `Infinity`(exponent 全 1,fraction 非 0),`0` 的判斷條件都一樣,所以可以直接回傳
* 若是 Normalized Number,可以直接捨棄 FP32 的後 16 bits,
* reference
* [各種小數表示方式](https://moocaholic.medium.com/fp64-fp32-fp16-bfloat16-tf32-and-other-members-of-the-zoo-a1ca7897d407)
* [wiki](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format)
---
### 測驗 2
---
### 測驗 3
---
### 測驗 4
#### [LeetCode 287. Find the Duplicate Number](https://leetcode.com/problems/find-the-duplicate-number/)
```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;
}
```
`CCC = c1 < c2`
#### 程式碼運作機制
* `log_2` 代表取幾次 `log` 會等於 0,用意是在計算需要考慮幾個 bits,因為題目有說到 n 個數的範圍都介在 0 ~ n-1,這些數都能用 log_2 個 bits 來表示
* 這題的用到概念跟 [quiz2](https://hackmd.io/MpKiicu-SkaYWrvn5gB2kA?view#%E6%B8%AC%E9%A9%976) 的第 6 小題概念類似,**每個 bit 都是獨立個個體**,可以分開來看待
* 假設陣列大小是 4,出現的整數範圍會是 [1,3],假設每個數都剛好出現一次會是下面的狀態:
| num | b~1~ | b~0~ |
|:---:|:----:|:----:|
| 0 | 0 | 0 |
| 1 | 0 | 1 |
| 2 | 1 | 0 |
| 3 | 1 | 1 |
| sum | 2 | 2 |
* 可以把上面這個狀態當作**基準點**看待,雖然 0 並不會出現在陣列中,但因為這個**基準點**是沒有出現重複的數,加入判斷會比較好懂,再舉個例,假設有重複的數字是 2,那狀態會如下
| num | b~1~ | b~0~ |
|:---:|:----:|:----:|
| 1 | 0 | 1 |
| 2 | 1 | 0 |
| 3 | 1 | 1 |
| 2 | 1 | 0 |
| sum | 3 | 2 |
* 有了重複的數字之後,形成的狀態再跟上面的**基準點**比較,不難發現每個大於基準點相對的 bit 剛好就是重複的數字有的 bit,在這個例子來說就是 **10**~(2)~
* 程式碼中的 `c1`, `c2` 就是分別在統計**基準點**跟**重複陣列**在每個 bit 出現 1 的次數,若 > 基準點就表示重複的數在這個 bit 也是 1,裡面的迴圈就是在實做這個部份,而外面的迴圈是在將每個 bit 跑過一遍
* 執行結果:
![](https://i.imgur.com/zgcDKWu.png)
#### 另解
* 1. 因為題目有給定數字範圍了,可以直接宣告一個 bool 陣列 `check[]`,其中 `check[i]` 代表 `i` 是否有出現過,若前面出現過就直接回傳 `i`
* 但缺點也很明顯,就是會使用過多的 memory
```c=
int findDuplicate(int* nums, int numsSize){
bool *check = malloc(sizeof(bool) * numsSize);
memset(check, false, numsSize);
for (int i = 0; i < numsSize; i++) {
if (check[nums[i]]) {
free(check);
return nums[i];
}
check[nums[i]] = true;
}
return -1;
}
```
執行結果
![](https://i.imgur.com/OSKfU9t.png)
* 2. 利用 [鴿籠原理](鴿籠原理) 和 [binary search](https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%88%86%E6%90%9C%E5%B0%8B%E6%BC%94%E7%AE%97%E6%B3%95),假設 arrSize 是 n ,出現數字範圍是 1 ~ n-1,利用 `low`, `high`, `mid` 來尋找重複出現的數字,初始定 `low` = 1, `high` = n - 1, `mid` = (`low` + `high`) / 2,每一輪利用 `mid` 當作標竿,`mid` 是 array 的 index,利用變數 `count` 統計陣列中 <= `mid` 的數字有幾個,每一輪統計完若 `count` > `mid`,根據鴿籠原理可以確認重複的數是落在小於 `mid` 這個區間的,此時再把搜索範圍改成 [1, mid],反之亦然,下面舉例:
* arrSize 為 9 的陣列,數字範圍會在 [1,8],初始 `low` = 1, `high` = 8, `mid` = 4,第一輪計算 <= 4 的 element 個數,發現 `count` = 6,<= 4 的數範圍只可能是 [1,4],確有 6 個數,代表重複的數必定在這個區間並將搜索區間更改,直到 `low` >= `hign` 為止,`low` 即為所求
* 這個方法的核心概念就是透過題目限制的出現的數範圍在 `1 ~ n - 1`,`n` 是 array size,再透過 array 的 `index` 來當作一個 `flag` 來尋找重複的數
* [reference](https://leetcode.com/problems/find-the-duplicate-number/discuss/72844/Two-Solutions-(with-explanation)%3A-O(nlog(n))-and-O(n)-time-O(1)-space-without-changing-the-input-array)
| index | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|:-----:| --- | --- | --- |:---:| --- | --- | --- | --- | --- |
| num | 1 | 7 | 5 | 6 | 4 | 2 | 3 | 3 | 3 |
```c=
int findDuplicate(int* nums, int numsSize){
int low = 1, high = numsSize - 1;
while (low < high) {
int mid = (low + high) / 2;
int count = 0;
for (int i = 0; i < numsSize; i++) {
if (nums[i] <= mid)
count++;
}
if (count > mid)
high = mid;
else
low = mid + 1;
}
return low;
}
```
執行結果
![](https://i.imgur.com/DvoGwtv.png)