簡體   English   中英

來自“Bit Twiddling Hacks”的 SWAR 字節計數方法——它們為什么有效?

[英]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 = 0x1234567811335577n = 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM