# 2017q1 Homework4 (clz)
contributed by <`xdennisx`>
## Count Leading Zero
- recursive version
- iteration version
- binary search technique
- byte-shift version
- Harley’s algorithm
一開始 make 的時候發現沒有 `install-git-hooks` 所以就把之前的複製貼上
> 加了發現大大增加 commit 的難度= =
- 原本的 for loop 會移動到一次最多 32 bit,這是不合法的
```c=
for(uint32_t j=(1<<i);j<(1<<(i+1));j++)
```
所以我改了一下
```c=
for (uint32_t j = (1 << i); j < (1 << ((i + 1) == 32 ? 31 : (i+1))); j++)
```
- `calculate.c` 的兩個 FILE 好像也是錯的
```c=
FILE *fptr = fopen("iteration.txt","r");
FILE *output = fopen("output.txt","w");
```
我也改了一下
```c=
FILE *fp = fopen("iteration.txt","r");
//FILE *output = fopen("output.txt","w");
```
- `main.c` 裡面的 output file 沒有初始化
```c=
FILE *output;
```
改成
```c=
FILE *output = NULL;
```
**終於給我 commit 了!!!**
先把原始圖的輸出
![](https://i.imgur.com/B9sLK2A.png)
![](https://i.imgur.com/zwQpM8J.png)
分析:
- recursive 演算法會有規率性的凸起
- harley、binary、byte 較穩定
- iteration 後面表現比前面好
### recursive version
```c=
#include "clz2.h"
static const int mask[]={0,8,12,14};
static const int magic[]={2,1,0,0};
unsigned clz2(uint32_t x,int c)
{
if (!x && !c) return 32;
uint32_t upper = (x >> (16>>c));
uint32_t lower = (x & (0xFFFF>>mask[c]));
if (c == 3) return upper ? magic[upper] : 2 + magic[lower];
return upper ? clz2(upper, c + 1) : (16 >> (c)) + clz2(lower, c + 1);
}
```
原理:
C 從 0~3 代入,一開始 **upper** 跟 **lower** 分別會把 32 bit 拆成 16+16 個 bit,之後就看 **upper** 是否為 0,不是 0 的話就只要把 upper 代入下一層 recursive,下一層就會再把 **upper** 有效的那些 bit 再拆成一半,到 `C==3` 的時候終止。如果 **upper** 等於 0,就只要把 **lower** 代入下一層 recursive,算出來的答案再加上當層 **upper** 的 bit 數就是答案。
### iteration version
```c=
include "clz.h"
unsigned clz(uint32_t x) {
int n = 32, c = 16;
do {
uint32_t y = x >> c;
if (y) { n -= c; x = y; }
c >>= 1;
} while (c);
return (n - x);
}
```
原理:
**c** 是切割的 bit 數,**y** 是類似 **upper** 的東西,如果 **y** 等於 1,就把要看的 bit 數縮小到 **(n-c)** 的範圍,再進入下一次 loop 的時候就只要看前面 **y** 個 bit 就好,最後再看首位是不是 0。
### binary search technique
```c=
#include "clz.h"
unsigned clz(uint32_t x) {
if (x == 0) return 32;
int n = 0;
if (x <= 0x0000FFFF) { n += 16; x <<= 16; }
if (x <= 0x00FFFFFF) { n += 8; x <<= 8; }
if (x <= 0x0FFFFFFF) { n += 4; x <<= 4; }
if (x <= 0x3FFFFFFF) { n += 2; x <<= 2; }
if (x <= 0x7FFFFFFF) { n += 1; x <<= 1; }
return n;
}
```
原理:
**if** 裡面的判斷式都是把目前在看的有效 bit 數砍半,例如 `x <= 0x0000FFFF` 就是判斷前16 bit 是不是 0,如果是都是 0,就把 n+16,接著繼續判斷後面 16 bit。如果不是 0,就把前 16 bit 的前面一半的 bit 判斷是否為 0,一直判斷到第一位 bit。
### byte-shift version
```c=
#include "clz.h"
unsigned clz(uint32_t x) {
if (x == 0) return 32;
int n = 1;
if ((x >> 16) == 0) { n += 16; x <<= 16; }
if ((x >> 24) == 0) { n += 8; x <<= 8; }
if ((x >> 28) == 0) { n += 4; x <<= 4; }
if ((x >> 30) == 0) { n += 2; x <<= 2; }
n = n - (x >> 31);
return n;
}
```
原理:
這也是一直把 32 bit 一直砍半的原理,`if ((x >> 16) == 0) { n += 16; x <<= 16; }` 意思就是如果前 16 bit 都是 0,那我答案就先加這 16 bit,之後就把這 16 個 0 踢掉不看,再來就看後面 16 bit 的一半,以此類推,最後 `x >> 31` 就是看第一位是不是 0 就是答案。
### Harley’s algorithm
```c=
#include "clz.h"
unsigned clz(uint32_t x)
{
static uint8_t const Table[] = {
0xFF, 0, 0xFF, 15, 0xFF, 1, 28, 0xFF,
16, 0xFF, 0xFF, 0xFF, 2, 21, 29, 0xFF,
0xFF, 0xFF, 19, 17, 10, 0xFF, 12, 0xFF,
0xFF, 3, 0xFF, 6, 0xFF, 22, 30, 0xFF,
14, 0xFF, 27, 0xFF, 0xFF, 0xFF, 20, 0xFF,
18, 9, 11, 0xFF, 5, 0xFF, 0xFF, 13,
26, 0xFF, 0xFF, 8, 0xFF, 4, 0xFF, 25,
0xFF, 7, 24, 0xFF, 23, 0xFF, 31, 0xFF,
};
/* Propagate leftmost 1-bit to the right */
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
/* x = x * 0x6EB14F9 */
x = (x << 3) - x; /* Multiply by 7. */
x = (x << 8) - x; /* Multiply by 255. */
x = (x << 8) - x; /* Again. */
x = (x << 8) - x; /* Again. */
return Table[x >> 26];
}
```
原理:
> 理解ing
## _Generic
一個**類似** C++ Template 的東西,C11 以 macro 為基礎的 type-generic functions,主要透過 **\_Generic** 關鍵字。
```c=
include <stdio.h>
void funci(int x) { printf("func value = %d\n", x); }
void funcc(char c) { printf("func char = %c\n", c); }
void funcdef(double v) { printf("Def func's value = %lf\n", v); }
#define func(X) \
_Generic((X), \
int: funci, char: funcc, default: funcdef \
)(X)
int main() {
func(1);
func('a');
func(1.3);
return 0;
}
```
輸出結果:
```
func value = 1
func value = 97
Def func's value = 1.300000
```
也可以套用在 `printf` 上
```c=
#define printf_dec_format(x) _Generic((x), \
char: "%c", \
signed char: "%hhd", \
unsigned char: "%hhu", \
signed short: "%hd", \
unsigned short: "%hu", \
signed int: "%d", \
unsigned int: "%u", \
long int: "%ld", \
unsigned long int: "%lu", \
long long int: "%lld", \
unsigned long long int: "%llu", \
float: "%f", \
double: "%f", \
long double: "%Lf", \
char *: "%s", \
void *: "%p")
#define print(x) printf(printf_dec_format(x), x)
#define printnl(x) printf(printf_dec_format(x), x), printf("\n");
```
輸出結果:
```
printnl('a'); // prints "97" (on an ASCII system)
printnl((char)'a'); // prints "a"
printnl(123); // prints "123"
printnl(1.234); // prints "1.234000"
```
在這邊 `'a'` 是 int,如果要讓他已字元方式輸出需要強制 cast 他
所以我把 `printf` 改掉
```c=
printf("%d %lu cycles\n", i, timecall / 100);
```
改成
```c=
print(i);
print(timecall / 100);
printnl("cycles");
```
改成這樣的優點就是不會在 format 的時候打錯對應的符號,造成輸出誤差
## Modularize the build system
運用 `%` 來簡化 `Makefile` 的內容,把產生執行檔的方式改寫一下
```shell
clz_%: %_method.o %.c
$(CC) $(CFLAGS_common) $? -o $@
%_method.o: $(SRCS_common)
$(CC) -c $(CFLAGS_common) $< -D$(shell echo $(subst _method.o,,$@)) -o $@
```
這樣以後就不用為了產生新的執行檔就要再多一條 rule
## clz 的應用場合
- [cpython 在 fractions 計算浮點的時候](https://github.com/python/cpython/blob/6f0eb93183519024cb360162bdd81b9faec97ba6/Modules/mathmodule.c#L1305)
- [壓縮 data,把前面一串 0 變成一個不為 0 的數字](https://en.wikipedia.org/wiki/Data_compression#Data_differencing)
- 利用 `clz(x − y) >> 5` 去計算 32 bit `x == y` 是不是對的,是 0 的話就是錯的,是 1 的話就是對的(下面網站寫錯了)
## Reference
- [clz 應用](https://en.wikipedia.org/wiki/Find_first_set#cite_ref-41)
- [你所不知道的 C 語言](https://hackmd.io/s/S1maxCXMl)
- [\_Generic](http://www.robertgamble.net/2012/01/c11-generic-selections.html)