从字节获取位

时间:2015-11-05 20:52:07

标签: c++ c++11 bitset

我有以下功能:

int GetGroup(unsigned bitResult, int iStartPos, int iNumOfBites)
{
 return (bitResult >> (iStartPos + 1- iNumOfBites)) & ~(~0 << iNumOfBites);
}

该函数返回一个字节的位组 即bitResult=102 (01100110)2, iStartPos=5, iNumOfBites=3
输出:2 (10)2
对于iStartPos=7, iNumOfBites=4
输出:3 (0110)2
我正在寻找更好的方式/&#34;友好&#34;这样做,即用bitset或类似的东西。
有任何建议吗?

3 个答案:

答案 0 :(得分:4)

(src >> start) & ((1UL << len)-1)     // or 1ULL << if you need a 64-bit mask

是从len开始表达start位提取的一种方法。 (在这种情况下,start是您想要的范围的LSB。您的函数需要MSB作为输入。)此表达式来自Wikipedia's article on the x86 BMI1 instruction set extensions

但是,如果len是该类型的整个宽度,则生成掩码的两种方式都存在风险。 (提取所有位的角落情况)。按类型的整个宽度移位可以产生零或不变。 (它实际上调用了未定义的行为,但实际上如果编译器在编译时无法看到它会发生什么。例如,x86将移位计数屏蔽到0-31范围(对于32位移位)。使用32位注意:

  • 如果1&lt;&lt; 32产生1,然后1-1 = 0,因此结果将为零。

  • 如果~0&lt;&lt; 32产生〜0而不是0,掩码将为零。

请记住1<<len len的未定义行为太大了:不像将其写为0x3ffffffffff或其他任何内容,都不会自动升级到long long,所以类型1很重要。

我认为从您的示例中您需要位[iStartPos : iStartPos - iNumOfBites],其中位从零开始编号。

我在函数中更改的主要内容是函数和变量的命名,并添加注释。

  • bitResult是函数的输入;不要使用&#34;结果&#34;以它的名字。
  • iStartPos好的,但有点冗长
  • iNumOfBites计算机具有位和字节。如果您正在处理叮咬,您需要医生(或牙医)。

此外,返回类型可能应为unsigned

// extract bits [msb : msb-len] from input into the low bits of the result
unsigned BitExtract(unsigned input, int msb, int len)
{
  return (input >> (msb-len + 1)) & ~(~0 << len);
}

如果你的起始位置参数是lsb而不是msb,表达式会更简单,代码会更小更快(除非这只会给调用者带来额外的工作)。使用LSB作为参数,BitExtract是7条指令,如果它是MSB(在x86-64,gcc 5.2上),则为9条。

还有一个机器指令(由Intel Haswell和AMD Piledriver引入)执行此操作。使用它可以获得更小,更快的代码。它还使用LSB,len位置约定,而不是MSB,因此您可以使用LSB作为参数获得更短的代码。

Intel CPU只知道需要先将一个立即加载到寄存器中的版本,因此当这些值是编译时常量时,与简单的移位和屏蔽相比,它不会节省太多。 e.g. see this post about using it or pextr for RGB32 -> RGB16。当然,如果start和len都是编译时常量,那么参数是否是所需范围的MSB或LSB并不重要。

只有AMD实现了bextr的版本,可以将控制掩码作为直接常量,但不幸的是,似乎gcc 5.2并没有将立即版本用于使用内在函数的代码(即使使用{ {1}}(即推土机v2又名打桩机)。(generate bextr with an immediate argument on its own in some cases-march=bdver2合作。)

tested it out on godbolt了解您使用或不使用bextr可获得的代码类型。

-march=bdver2

需要额外的指令(#include <immintrin.h> // Intel ICC uses different intrinsics for bextr // extract bits [msb : msb-len] from input into the low bits of the result unsigned BitExtract(unsigned input, int msb, int len) { #ifdef __BMI__ // probably also need to check for __GNUC__ return __builtin_ia32_bextr_u32(input, (len<<8) | (msb-len+1) ); #else return (input >> (msb-len + 1)) & ~(~0 << len); #endif } )来实现movzx安全检查,以避免起始字节溢出到长度字节中。我把它排除在外是因为要求0-31范围之外的起始位是无稽之谈,更不用说0-255范围了。由于它不会崩溃,只是返回一些其他废话结果,没有多大意义。

无论如何,(msb-len+1)&0xff保存了不少指令(如果BMI2 bext / shlx也不可用!在Godbolt上的shrx是Haswell,因此包括BMI2也是如此。)

但是英特尔CPU上的 -march=native解码为2 uops http://agner.org/optimize/),所以与bextr /相比它根本不是很有用shrx,但保存一些代码大小除外。 and实际上对吞吐量(1 uop / 3c延迟)更好,即使它是一种更强大的指令。但是,延迟更糟糕。并且AMD CPU的运行速度非常慢pext,但pext为单个uop。

答案 1 :(得分:1)

pragma pack(push, 1)
struct Bit
{
union
    {
    uint8_t _value;
    struct {
        uint8_t _bit0:0;
        uint8_t _bit1:0;
        uint8_t _bit2:0;
        uint8_t _bit3:0;
        uint8_t _bit4:0;
        uint8_t _bit5:0;
        uint8_t _bit6:0;
        uint8_t _bit7:0;
        };
    };
};
#pragma pack(pop, 1)
typedef Bit bit;

struct B
{
    union
    {
            uint32_t _value;
            bit bytes[1];    // 1 for Single Byte
    };
};

使用struct和union,您可以将Struct B _value设置为结果,然后为每个0或1访问byte [0] ._ bit0到byte [0] ._ bit7,反之亦然。设置每个位,结果将在_value中。

答案 2 :(得分:1)

我可能会做以下的事情,以便为参数中的错误提供额外的保护,并减少转移的数量。

我不确定我是否理解您使用的参数的含义,因此可能需要稍微调整一下。

我不确定这是否必须更有效率,因为为安全起见,有许多决定和范围检查。

/*
 *  Arguments:    const unsigned bitResult byte containing the bit field to extract
 *                const int iStartPos  zero based offset from the least significant bit
 *                const int iNumOfBites  number of bits to the right of the starting position
 *
 *  Description: Extract a bitfield beginning at the specified position for the specified
 *               number of bits returning the extracted bit field right justified.
*/
int GetGroup(const unsigned bitResult, const int iStartPos, const int iNumOfBites)
{
    // masks to remove any leading bits that need to disappear.
    // we change starting position to be one based so the first element is unused.
    const static unsigned bitMasks[] = {0x01, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};

    int iStart = (iStartPos > 7) ? 8 : iStartPos + 1;
    int iNum = (iNumOfBites > 8) ? 8 : iNumOfBites;

    unsigned retVal = (bitResult & bitMasks[iStart]);
    if (iStart > iNum) {
        retVal >>= (iStart - iNum);
    }

    return retVal;
}