是否有一种聪明的(即:无分支)方式来“压缩”十六进制数。基本上将所有0都移到一边?
例如:
0x10302040 -> 0x13240000
或
0x10302040 -> 0x00001324
我看了Bit Twiddling Hacks,但没有看到任何内容。
这是一个SSE数值旋转算法。我需要删除任何变为0的枢轴。我可以使用_mm_cmpgt_ps
找到好的枢轴,_mm_movemask_ps
将其转换为掩码,然后进行攻击以获得类似上面的内容。十六进制值进入掩码,以便_mm_shuffle_ps
指令在SSE 128位寄存器上执行置换。
答案 0 :(得分:7)
计算_pext
的掩码:
mask = arg;
mask |= (mask << 1) & 0xAAAAAAAA | (mask >> 1) & 0x55555555;
mask |= (mask << 2) & 0xCCCCCCCC | (mask >> 2) & 0x33333333;
首先进行位或成对位,然后进行四边形。掩码可防止移位值溢出到其他数字。
以这种方式计算面具或者以某种方式计算(可能更快)你不需要_pext
的全部功能,所以如果有针对性的硬件不支持你可以用这个代替它:
for(int i = 0; i < 7; i++) {
stay_mask = mask & (~mask - 1);
arg = arg & stay_mask | (arg >> 4) & ~stay_mask;
mask = stay_mask | (mask >> 4);
}
如果有一些空格,每次迭代都会将所有半字节向右移动一位数。 stay_mask
标记位于其最终位置的位。这比Hacker的Delight解决方案使用的操作少一些,但仍可能从分支中受益。
答案 1 :(得分:3)
假设我们可以使用_pext_u32
,那么问题是计算一个掩码,对于每个非零的半字节都有一个F.我不确定最佳方法是什么,但是你可以计算半字节的4位的OR,然后将它“扩散”回F,就像这样:
// calculate horizontal OR of every nibble
x |= x >> 1;
x |= x >> 2;
// clean up junk
x &= 0x11111111;
// spread
x *= 0xF;
然后将其用作_pext_u32
的掩码。
_pext_u32
可以通过此模拟(取自Hacker's Delight,图7.6)
unsigned compress(unsigned x, unsigned m) {
unsigned mk, mp, mv, t;
int i;
x = x & m; // Clear irrelevant bits.
mk = ~m << 1; // We will count 0's to right.
for (i = 0; i < 5; i++) {
mp = mk ^ (mk << 1); // Parallel prefix.
mp = mp ^ (mp << 2);
mp = mp ^ (mp << 4);
mp = mp ^ (mp << 8);
mp = mp ^ (mp << 16);
mv = mp & m; // Bits to move.
m = m ^ mv | (mv >> (1 << i)); // Compress m.
t = x & mv;
x = x ^ t | (t >> (1 << i)); // Compress x.
mk = mk & ~mp;
}
return x;
}
但这有点像灾难。那么只需要采用分支代码就可能更好。
答案 2 :(得分:2)
uint32_t fun(uint32_t val) {
uint32_t retVal(0x00);
uint32_t sa(28);
for (int sb(28); sb >= 0; sb -= 4) {
if (val & (0x0F << sb)) {
retVal |= (0x0F << sb) << (sa - sb)
sa -= 4;
}
}
return retVal;
}
我认为这个(或类似的东西)就是你要找的东西。消除数字中的0个半字节。我没有调试它,它只适用于一侧atm。
答案 3 :(得分:2)
如果您的处理器支持条件指令执行,您可以从此算法中获益:
uint32_t compact(uint32_t orig_value)
{
uint32_t mask = 0xF0000000u; // Mask for isolating a hex digit.
uint32_t new_value = 0u;
for (unsigned int i = 0; i < 8; ++i) // 8 hex digits
{
if (orig_value & mask == 0u)
{
orig_value = orig_value << 4; // Shift the original value by 1 digit
}
new_value |= orig_value & mask;
mask = mask >> 4; // next digit
}
return new_value;
}
这看起来像是循环展开的好选择。
该算法假定当原始值向左移动时,零移入,填充&#34;空&#34;位。
编辑1: 在支持条件执行指令的处理器上,根据原始值和掩码的AND运算结果,有条件地执行原始值的移位。因此没有分支,只会忽略指示。
答案 4 :(得分:-2)
我提出了以下解决方案。请看一下,也许它会对你有帮助。
#include <iostream>
#include <sstream>
#include <algorithm>
using namespace std;
class IsZero
{
public:
bool operator ()(char c)
{
return '0' == c;
}
};
int main()
{
int a = 0x01020334; //IMPUT
ostringstream my_sstream;
my_sstream << hex << a;
string str = my_sstream.str();
int base_str_length = str.size();
cout << "Input hex: " << str << endl;
str.insert(remove_if(begin(str), end(str), IsZero()), count_if(begin(str), end(str), IsZero()), '0');
str.replace(begin(str) + base_str_length, end(str), "");
cout << "Processed hex: " << str << endl;
return 0;
}
输出:
Input hex: 1020334
Processed hex: 1233400