[英]SWAR byte counting methods from 'Bit Twiddling Hacks' - why do they work?
Bit Twiddling Hacks包含以下宏,它們計算字x
中小於或大於n
的字節n
:
#define countless(x,n) \
(((~0UL/255*(127+(n))-((x)&~0UL/255*127))&~(x)&~0UL/255*128)/128%255)
#define countmore(x,n) \
(((((x)&~0UL/255*127)+~0UL/255*(127-(n))|(x))&~0UL/255*128)/128%255)
但是,它沒有解釋它們為什么起作用。 這些宏背后的邏輯是什么?
讓我們嘗試對countmore
直覺。
首先, ~0UL/255*(127-n)
是將值127-n
並行復制到字中所有字節的巧妙方法。 為什么有效? ~0
在所有字節中為 255。 因此, ~0/255
在所有字節中都是1
。 乘以(127-n)
執行開頭提到的“復制”。
術語~0UL/255*127
只是上述n
為零的特殊情況。 它將 127 復制到所有字節中。 如果字是 4 個字節,那就是0x7f7f7f7f
。 帶有x
“Anding”將每個字節中的高位清零。
這是第一項(x)&~0UL/255*127)
。 結果與x
相同,只是每個字節中的高位為零。
第二項~0UL/255*(127-(n))
如上: 127-n
復制到每個字節。
對於任何給定的字節x[i]
,如果x[i]<=127
,將這兩項相加得到127-n+x[i]
。 每當x[i]>n
時,此數量將設置高位。 最容易將其視為將兩個 7 位無符號數相加。 結果“溢出”到第 8 位,因為結果是 128 或更多。
所以看起來算法將使用每個字節的第 8 位作為布爾值,指示x[i]>n
。
那么另一種情況x[i]>127
呢? 這里我們知道字節大於n
因為算法規定n<=127
。 第 8 位應該始終為 1。令人高興的是,總和的第 8 位並不重要,因為下一步與x
的結果“或”。 由於x[i]
將第 8 位設置為 1 當且僅當它為 128 或更大時,此操作“強制”第 8 位為 1,就在總和可能提供錯誤值時。
總結到目前為止,當且僅當x[i]>n
,“或”結果的第 i 個字節中的第 8 位設置為 1。 好的。
下一個操作&~0UL/255*128
將所有內容設置為零,除了所有感興趣的第 8 位。 它是與 0x80808080 的“anding”...
現在的任務是找到設置為 1 的這些位的數量。為此, countmore
使用了一些基本的數論。 首先它右移 7 位,所以感興趣的位是 b0、b8、b16……這個字的值是
b0 + b8*2^8 + b16*2^16 + ...
一個美麗的事實是 1 == 2^8 == 2^16 == ... mod 255。換句話說,每個 1 位是 1 mod 255。因此,找到移位結果的 mod 255 與求和 b0+b8+b16+...
哎呀。 我們完成了。
來分析countless
宏。 我們可以將這個宏簡化為以下代碼:
#define A(n) (0x0101010101010101UL * (0x7F+n))
#define B(x) (x & 0x7F7F7F7F7F7F7F7FUL)
#define C(x,n) (A(n) - B(x))
#define countless(x,n) (( C(x,n) & ~x & 0x8080808080808080UL) / 0x80 % 0xFF )
A(n)
將是:
A(0) = 0x7F7F7F7F7F7F7F7F
A(1) = 0x8080808080808080
A(2) = 0x8181818181818181
A(3) = 0x8282828282828282
....
和B(x)
,每個字節x
將與掩模0x7F
。 如果我們假設x = 0xb0b1b2b3b4b5b6b7
並且n = 0
,那么C(x,n)
將等於0x(0x7F-b0)(0x7F-b1)(0x7F-b2)...
例如,我們假設x = 0x1234567811335577
和n = 0x50
。 所以:
A(0x50) = 0xCFCFCFCFCFCFCFCF
B(0x1234567811335577) = 0x1234567811335577
C(0x1234567811335577, 0x50) = 0xBD9B7957BE9C7A58
~(0x1234567811335577) = 0xEDCBA987EECCAA88
0xEDCBA987EECCAA88 & 0x8080808080808080UL = 0x8080808080808080
C(0x1234567811335577, 0x50) & 0x8080808080808080 = 0x8080000080800000
(0x8080000080800000 / 0x80) % 0xFF = 4 //Count bytes that equal to 0x80 value.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.