压缩十六进制数

时间:2014-07-02 19:02:06

标签: c++ bit-manipulation sse

是否有一种聪明的(即:无分支)方式来“压缩”十六进制数。基本上将所有0都移到一边?

例如:

0x10302040 -> 0x13240000

0x10302040 -> 0x00001324

我看了Bit Twiddling Hacks,但没有看到任何内容。

这是一个SSE数值旋转算法。我需要删除任何变为0的枢轴。我可以使用_mm_cmpgt_ps找到好的枢轴,_mm_movemask_ps将其转换为掩码,然后进行攻击以获得类似上面的内容。十六进制值进入掩码,以便_mm_shuffle_ps指令在SSE 128位寄存器上执行置换。

5 个答案:

答案 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