contributed by <maskashura
>
jcyztw
if
statements 會用那些數值(例如:0x0000FFFF)來做判斷?(提示:可與 binary search 作聯想。這個問題我去找老師討論作業時有被問過,當下我整個人愣住,直到老師給我提示才知道怎麼回答XD)lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 142
Model name: Intel(R) Core(TM) i5-7300U CPU @ 2.60GHz
Stepping: 9
CPU MHz: 1067.541
CPU max MHz: 3500.0000
CPU min MHz: 400.0000
BogoMIPS: 5424.00
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 3072K
NUMA node0 CPU(s): 0-3
n 為輸入值的位元數中, c 為移動的位元數,會一次依序向右移動16,8,4,2,1 bits , 用類似 binary search 方式做檢查, 若位移後檢查為0則回傳 (n-x),即得二進位值開頭有幾個零
static inline __attribute((always_inline))
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);
}
e.g.
輸入 x=0x12345678, 即為二進位值 00010010001101000101011001111000
當 c=16
y=00000000000000000001001000110100
判斷y不為0, 執行下一次位移
當 c=8
y=00000000000000000000000000010010
判斷y不為0, 執行下一次位移
當 c=4
y=00000000000000000000000000000001
判斷y不為0, 執行下一次位移
當 c=2
y=00000000000000000000000000000000
判斷y為0, 執行
if (y) {
n -= c;
x = y;
}
當 c=1
y=00000000000000000000000000000000
判斷y為0, 再次執行
if (y) {
n -= c;
x = y;
}
最後回傳(n - x) = 3 (二進位值開頭有3個零)
操作手法同 "iteration.c" , 只是把 while 打散成5組 if ,判斸是否在右半邊, 再將 n 值做回傳
static inline __attribute((always_inline))
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;
}
操作方式仍和 "iteration.c" 一樣, 和 binary search 差別只有條件改用 == 判斷
static inline __attribute((always_inline))
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;
}
為 iterative 的遞迴版本, c 為右移 bits 數
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);
/* shift成功代表leading zero的數量不足count,shift之後繼續用更小的count去檢查 shift不成功代表leading zero的數量足夠count,用shift前的值繼續檢查,而count<<1則把該次檢查計算的leading zero數量加上*/
}
為一種 Hash table查表法
static inline __attribute((always_inline))
unsigned clz(uint32_t x)
{
#ifdef CTZ
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,
};
#else
static uint8_t const Table[] = {
32,31, 0,16, 0,30, 3, 0,15, 0, 0, 0,29,10, 2, 0,
0, 0,12,14,21, 0,19, 0, 0,28, 0,25, 0, 9, 1, 0,
17, 0, 4, 0, 0, 0,11, 0,13,22,20, 0,26, 0, 0,18,
5, 0, 0,23, 0,27, 0, 6,0,24, 7, 0, 8, 0, 0, 0
};
#endif
/* 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);
/*把最高位後的位元全部補為1*/
/* 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)];
/*用前面6個位元表示leading zero有幾個*/
}
參考資料