contributed by < Rsysz
>
sysprog
根據C99標準的6.5.7節
“The result of
E1
>>E2
is E1 right-shifted E2 bit positions. … If E1 has a signed type and a negative value, the resulting value is implementation-defined.
對有號型態的物件進行算術右移(Arithmetic Shift Right)屬於 Unspecified Behavior
因此想實作一套可跨平台的ASR,代表當輸入為負數時右移必須補 1 ,反之則必須補 0
根據題意,已知m為輸入數值,n為右移次數,由 return
回推
m
為正數若 m >> n
為補0,則 fix ^ (fix >> n)
= 0fix = 0
相當於 fixu = 0
代表 logical
必須等於 0logical
為測試此平台上負數右移補 0 或 1 , OP1 = (b) >> 1
m
為負數若 m >> n
為補0,則 fix ^ (fix >> n)
必須補上對應 n
位的 1logical
為 1 代表右移補 0 , fix = -OP2
又 m < 0
時 fix
必須等於 -1(c) m < 0
以為例, m = -5
, n = 1
若假設右移為 邏輯右移
因 m
為負數, fix = fixu = -1
,因此 num > 0
, 因此 (num & (num - 1)) == 0
Built-in Function:
int __builtin_ctz (unsigned int x)
- Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined.
Built-in Function:
int __builtin_clz (unsigned int x)
- Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined.
Binary | |
---|---|
0000 0001 | |
0000 0100 | |
0001 0000 |
已知
以 !(__builtin_ctz(num) OPQ)
確保尾端數來皆是偶數個 0 排除 2 的次方數
因此 OPQ = (f) & 0x1
透過 0x55555555
確保 num
的二進位只有奇數位有值,又 4 的次方數轉為二進位只有一個 1 於是再排除有複數位的情況。
找出 MSB 後有效位並對 1 求補數,利用 1 << (32 - __builtin_clz(N)) - 1
製造有效位 Mask 並求剩下的數。
針對 LeetCode 1342. Number of Steps to Reduce a Number to Zero,我們可運用 gcc 內建函式實作,假設 int 寬度為 32 位元,程式碼列表如下:
__builtin_popcount(x)
: 計算 x 的二進位表示中,總共有幾個1
參考題二得知對應的__builtin_clz(num)
為計算 MSB 開始到第一個 1 之間 0 的個數
透過32 - __builtin_clz(num)
求得 MSB 與 0 的距離,再以 __builtin_popcount(x) - 1
求得 MSB 以外的 1 與 0 的距離,因此 AAA = __builtin_popcount(num)
,BBB = __builtin_clz(num)
於 __builtin_clz(num | 1)
針對 LSB 為 0 的情況加 1 ,而避免了傳入 0
每 byte
切割輸入值最終加總為 popcount
,而下方 clz
則可以計算 __builtin_clz
的相反數值
參考Find the log base 2 of an N-bit integer in O(lg(N)) operations
考慮以下 64-bit GCD (greatest common divisor, 最大公因數) 求值函式:
改寫為以下等價實作:
上述程式就是在實作輾轉相除法,首先透過 shift
判斷當 u
和 v
都是偶數時,不斷除以 2 直到一方變成奇數,再對 u
單獨除到變為奇數,接著以不斷將兩數中較小的數置於 u
不斷以 v -= u
對 v
取餘,因此最終XXX = (b) v
YYY = (e) u << shift
以上 GCD 加速方法可以參考Binary GCD利用
binary gcd 的精神就是當兩數為偶數時,必有一 因子。
且一數為奇數另一數為偶數,則無 因子。
於是我們可以改良為:
透過 shift
加速 GCD 運算,最終於 return u << shift
補回先前右移的數值
透過 __builtin_ctz
改寫除 2 部分加速整體運算速度,然而透過 perf stat
指令查看兩者在處理極大的值時消耗的時間相差甚少,因此於 main
新增
以大量偶數數據帶入運算針對 __builtin_ctz
與題目所提供的 shift
方法比較計算速度
才能得到較為顯著的測試結果,採用 __builtin_ctz
明顯減少了 CPU cycles
, fetch instructions
和 branch
的次數,得到效能的提升。這裡值得注意的是如果使用 printf
將得到的數值印出,將會消耗大量的時間,因此強烈不推薦使用。
在影像處理中,bit array (也稱 bitset
) 廣泛使用,考慮以下程式碼:
可用 clz 改寫為效率更高且等價的程式碼:
上述程式碼相互對應後 int r = __builtin_ctzll
取代 if ((bitset >> i) & 0x1)
的功能,但改寫後的程式碼對 out[pos++]
賦值後還必須 clear bitset 的 LSB 以確保下個 Loop 的正確運行
根據題三提到
利用 bitset & -bitset
留下 LSB 再於 bitset ^= t
清除 LSB
因此 KKK = (e) bitset & -bitset