contributed by < CYT701 >
pow2()
讀入一個 8 位元無號數 e
,並回傳將 64 位元無號數 1 左移 e
的值。
next_pow2()
讀入一個 64 位元無號數 x
,並回傳最接近且大於等於 x
的 2 的冪的值
原題目敘述中 最接近且大於等於 2 的冪的值
應寫為 最接近且大於等於 x 的 2 的冪的值
才符合題意
這段程式碼是在 2lo 與 2hi 之間找尋欲求的值,當 lo < hi
時令 temp = (lo + hi) / 2
,不斷比較 x
與 2test 的大小直到 lo >= hi
:
題目中說明這段程式碼用以將原最高位元到最低位元中,所有的位元設定為 1 ,在這裡 x |= x >> 1;
同義於 x = x | (x >> 1);
所以可以推斷 AAAA
為 8 , BBBB
為 32
由於回傳值為最接近且大於等於 x
的 2 的冪的值,而經過剛才的運算,此時可能出現兩種情況:
x
的初始值是 2 的冪次,此時應回傳 x
的初始值x
的初始值已經被運算過後的 x
值覆蓋,無法求出其初始值,判斷應該是題目要求有誤temp
用以儲存 x
的初始值即可解決,利用運算後的 x
加 1 後右移 1 位元與 temp
作比較,若相等則表示 x
的初始值本來就是 2 的冪次,此時回傳 temp
x
的初始值不是 2 的冪次,此時回傳 x + 1
根據以上兩點可以得到以下判斷式
也可寫成
故原程式碼應改寫如下:
x = 0
的情況有誤加入判斷條件來處理 x = 0
的情況
__builtin_clzl
改寫在 6.59 Other Built-in Functions Provided by GCC 中說明:
所以要看到 __builtin_clz
:
簡單來說就是 __builtin_clz
會回傳最高位的 1 左邊有幾個 0 ,可以此判斷找出最高位的 1 在第幾個位元,而 __builtin_clzl
是 __builtin_clz
的 unsigned long
版本,實作如下:
在參考資料中提到: If x is 0, the result is undefined. 所以在實作時印出 __builtin_clzl(0)
的結果,得到 64
所以當 x = 0
時要回傳最接近且大於等於 0 的的 2 的冪次為 1
如果 x
不是 0 則要檢查 x
本身是否為 2 的冪次,如果是則回傳 x
否則回傳(uint64_t)1 << 63 - __builtin_clzl(x) + 1
結果如下:
Using dichotomy 為題目原本給的函式所產生
Using bitshift_origin 為題目原本的 bitshift 程式碼產生
Using bitshift_new 為修正後的 bitshift 程式碼
Using builtin 為使用 __builtin_clzl()
所產生
可以看到題目原本的 bitshift 程式碼在輸入值原本就是2的冪次時,會得到錯誤的結果
這裡在輸入為 0 時,__builtin_clzl(0)
與 __builtin_clzl(x)
的輸出居然不同,不確定為什麼會有這種狀況
LeetCode 1680. Concatenation of Consecutive Binary Numbers
給定一整數 n ,回傳將 1 到 n 的二進位表示法依序串接在一起所得到的二進位字串,其所代表的十進位數字 mod 109 + 7 之值
i
的 rightmost set bit 設為 0 ,所得到的值如果為 0 表示 i
為 2 的冪次,因為 2 的冪次的二進位表示只包含一個 1 ,其餘為 0 ,`i
的長度會比 i-1
增加 1 ,所以 len
要加 1len
代表的是此時 i
共有幾位元(不包含 leftmost set bit 左邊的 0),也同時代表在把下個數字串接上時 ans
需要左移的位元數根據程式碼,可以看出當 !(DDDD)
(也就是 DDDD == 0
)時 len++
,此時 i
為 2 的冪次,即 i
將 rightmost set bit 設為 0 後的值為 0 ,故 DDDD
為「將 rightmost set bit 設為 0 」這個動作。
你所不知道的 C 語言:數值系統篇在運用 bit-wise operator
處提到,在 C 語言中 x & (x - 1) == 0
的數學意義表示 x 為 2 的冪次
所以 DDDD
為 i & (i - 1)
接下來要將 i
串接到 ans
,所以要將ans
左移 len
後與 i
作 OR 運算
所以 EEEE
為 ans << len
__builtin_{clz,ctz,ffs}
改寫在 6.59 Other Built-in Functions Provided by GCC 中說明:
x
為 0 則回傳 0改寫如下:
由於 __built_clz()
回傳 leftmost set bit 左邊的 0 的數量,而 __builtin_ctz()
回傳 rightmost set bit 右邊的 0 數量,所以當 i
為 2 的冪次時,兩者的和會是總位元數減 1 ,又因為 i
是 int
型態,其範圍在 -231 與 231 - 1 之間,也就是 32 bits ,所以判斷是否為 2 的冪次的條件可改寫成 if (__builtin_clz(i) + __builtin_ctz(i) == 31)
,而 len
為當前 i
的位元數(不包含 leftmost set bit 左邊的 0 ),也就是從右邊開始數到 leftmost set bit 的值,即為 LSB 的 index ,故使用 __builtin_ffs()
要檢查 x
與 y
兩個 32 位元的數字是否都是奇數,將兩者分別與 1 作 AND 運算,兩者都是 1 則回傳true
利用特製的 bitmask SWAR_ODD_MASK
,將 1 賦型為 long 後左移 32 位元後加 1 ,然後檢查將 x
與 y
合併成 64 位元後與 SWAR_ODD_MASK
作 AND 運算,若與 SWAR_ODD_MASK
相等則回傳 true
ASCII | 0xxx.xxxx |
---|---|
2 bytes | 110x.xxxx 10xx.xxxx |
3 bytes | 1110.xxxx 10xx.xxxx 10xx.xxxx |
4 bytes | 1111.0xxx 10xx.xxxx 10xx.xxxx 10xx.xxxx |
UTF-8 的多位元組字元有以下特性:
所以要確認 UTF-8 的字串共有多少字元,可以用總字串長度減去後續位元組的數量
將字串( char *
)以二補數的形式轉換為有號整數的位元組( int8_t
),再跟 -65 作比較以確認是否為後續位元組,若不是後續位元組則 counter++
,最後回傳 counter 即為後續位元組數量
由於後續位元組的第 6 位元必為 0 ,第 7 位元必為 1 (最低位元為第 0 位元),也就是 not bit6 and bit7 ,所以可以以下步驟實作 SWAR
x
,回傳 x
的 set bit 數SWAR 實作如下:
buf
轉型為 uint64_t
的形態,並利用 end
指向位元組總長度除以 8 的位置 <補圖>所以可以推斷上面這段程式碼就是總字串長度,而 len
這個變數就是字串總長度,所以上述運算做完後依然會等於 len
,故 1 << AAAA
後為 8
所以 AAAA
為 3
這裡由於位元組數量有可能不整除 8 ,所以如果有剩下的位元組則利用 count_utf8()
來計算, 沒有的話 count 就維持原本的值
所以 BBBB
為 7 , CCCC
為 7 , DDDD
為 0
原本的 count_utf8()
一次只能處理 8 bits ,但 SWAR 實作則可以一次處理 64 bits
利用 for 迴圈,每次將 x
左移 1 ,若 x
與 0x8000 作 AND 運算後為 0 則回傳 false ,若可以完整跑完 for 迴圈並不回傳 false ,則回傳 true
也就是說只有在 x 的 leftmost bit 為 1 且 leftmost bit 與其他的 set bit 為連續出現,也就是所有的1都是連續的才會回傳 true
符合的樣式如下:
改寫如下:
首先釐清程式碼要做到的是:
x
的 leftmost bit 為 1x
的 1 必連續出現滿足以上回傳 true ,否則 false
初步想法是將 x
與 0x0000 或是 0xffff 作 XOR 運算,但 x ^ 0x0000 = x
,對於判斷不會有幫助,而 x ^ 0xffff = ~x
會將 x
的 1 變為 0 , 0 變為 1 ,所以若 x
符合樣式時, x ^ 0xffff
會是 rightmost bit 為 1 且所有 1 都連續的數,此時加 1 則會使整串位元組剩下一個 1 ,且這個 1 就是在 x
的 rightmost set bit ,故可知:
EEEE
為 ~(x) + 1
, FFFF
為 x