C++ 已设置MSB的第一个字节的索引

C++ 已设置MSB的第一个字节的索引,c++,bit-manipulation,C++,Bit Manipulation,我有8个8位值存储在64位整数中。每个字节的MSB可以是1或0,其余的位都是0。例如: MSB 10000000 00000000 10000000。。。1000000000000000000000000 LSB 我现在需要找到设置了位的第一个字节的索引。第一个意思是我们从最不重要的方向搜索。在上面的例子中,结果是2 使用de Bruijn,我们可以扫描第一个设置位并除以8以获得其字节索引 我的问题是:de Bruijn是通用的,它适用于任何输入。但在我的用例中,我们仅限于设置了MSB的字节。是

我有8个8位值存储在64位整数中。每个字节的MSB可以是1或0,其余的位都是0。例如:

MSB 10000000 00000000 10000000。。。1000000000000000000000000 LSB

我现在需要找到设置了位的第一个字节的索引。第一个意思是我们从最不重要的方向搜索。在上面的例子中,结果是2

使用de Bruijn,我们可以扫描第一个设置位并除以8以获得其字节索引

我的问题是:de Bruijn是通用的,它适用于任何输入。但在我的用例中,我们仅限于设置了MSB的字节。是否可以针对这种情况进行优化

实现在C++中。我不能使用任何内部函数或内联程序集(\u BitScanForward64(),\u builtin\u clzll等)。

(编辑) 隔离最低设置位
x&=(-x)
,然后查看哪个位正在检查这个确切的问题(尽管有标题)

下面的答案略为笼统


通过消除表格查找,可以在de Bruijn位扫描上节省几个延迟周期

uint64_t ByteIndexOfLowestSetBit(uint64_t val) {
    assert(val != 0);
    const uint64_t m = UINT64_C(0x0101010101010101);
    return ((((val - 1) ^ val) & (m - 1)) * m) >> 56;
}
使用尾随位操作获得覆盖最低设置位及其以下的掩码。 将掩码覆盖的每个字节设置为
1
。通过前缀水平求和,计算我们有多少
1
字节。现在,我们在u64字的最高有效字节中放置了一个基于1的字节索引。将计数移到底部,然后减去
1
,得到一个基于0的索引。但是,我们不希望关键路径上出现
-1
。。。因此,取而代之的是从
m
中减去
1
,这样我们就不会计算总数中的最低有效字节


查找最高集MS1B的问题更复杂,因为我们没有任何位操作技巧来隔离所需的位。那么,, ,将它们用作表的索引。如果不允许输入值为零,则最低有效字节的值不重要或不为零。这允许使用具有7位索引而不是8位索引的查找表

根据需要进行调整

uint64_t ReversedIndexOf_Highest_Byte_With_LSB_Set (uint64_t val) {
    static const unsigned char ctz7_tab[128] = {
        7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
    };
    assert(val != 0);
    assert((val & 0xFEFEFEFEFEFEFEFEULL) == 0);
    val = (val * UINT64_C(0x0080402010080402)) >> 57;
    return ctz7_tab[val];
}
(编辑) 隔离最低设置位
x&=(-x)
,然后查看哪个位正在检查这个确切的问题(尽管有标题)

下面的答案略为笼统


通过消除表格查找,可以在de Bruijn位扫描上节省几个延迟周期

uint64_t ByteIndexOfLowestSetBit(uint64_t val) {
    assert(val != 0);
    const uint64_t m = UINT64_C(0x0101010101010101);
    return ((((val - 1) ^ val) & (m - 1)) * m) >> 56;
}
使用尾随位操作获得覆盖最低设置位及其以下的掩码。 将掩码覆盖的每个字节设置为
1
。通过前缀水平求和,计算我们有多少
1
字节。现在,我们在u64字的最高有效字节中放置了一个基于1的字节索引。将计数移到底部,然后减去
1
,得到一个基于0的索引。但是,我们不希望关键路径上出现
-1
。。。因此,取而代之的是从
m
中减去
1
,这样我们就不会计算总数中的最低有效字节


查找最高集MS1B的问题更复杂,因为我们没有任何位操作技巧来隔离所需的位。那么,, ,将它们用作表的索引。如果不允许输入值为零,则最低有效字节的值不重要或不为零。这允许使用具有7位索引而不是8位索引的查找表

根据需要进行调整

uint64_t ReversedIndexOf_Highest_Byte_With_LSB_Set (uint64_t val) {
    static const unsigned char ctz7_tab[128] = {
        7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
        4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 
    };
    assert(val != 0);
    assert((val & 0xFEFEFEFEFEFEFEFEULL) == 0);
    val = (val * UINT64_C(0x0080402010080402)) >> 57;
    return ctz7_tab[val];
}
这里有一个简单的方法

int LeastSignificantSetBitByteIndex(long value)
{
    if((value & 0x80) != 0) return 0;
    if((value & 0x8000) != 0) return 1;
    if((value & 0x800000) != 0) return 2;
    if((value & 0x80000000L) != 0) return 3;
    if((value & 0x8000000000L) != 0) return 4;
    if((value & 0x800000000000L) != 0) return 5;
    if((value & 0x80000000000000L) != 0) return 6;
    if((value & 0x8000000000000000L) != 0) return 7;
    return -1;
}
    
int MostSignificantSetBitByteIndex(long value)
{
    if((value & 0x8000000000000000L) != 0) return 0;
    if((value & 0x80000000000000L) != 0) return 1;
    if((value & 0x800000000000L) != 0) return 2;
    if((value & 0x8000000000L) != 0) return 3;
    if((value & 0x80000000L) != 0) return 4;
    if((value & 0x800000) != 0) return 5;
    if((value & 0x8000) != 0) return 6;
    if((value & 0x80) != 0) return 7;
    return -1;
}
这里有一个简单的方法

int LeastSignificantSetBitByteIndex(long value)
{
    if((value & 0x80) != 0) return 0;
    if((value & 0x8000) != 0) return 1;
    if((value & 0x800000) != 0) return 2;
    if((value & 0x80000000L) != 0) return 3;
    if((value & 0x8000000000L) != 0) return 4;
    if((value & 0x800000000000L) != 0) return 5;
    if((value & 0x80000000000000L) != 0) return 6;
    if((value & 0x8000000000000000L) != 0) return 7;
    return -1;
}
    
int MostSignificantSetBitByteIndex(long value)
{
    if((value & 0x8000000000000000L) != 0) return 0;
    if((value & 0x80000000000000L) != 0) return 1;
    if((value & 0x800000000000L) != 0) return 2;
    if((value & 0x8000000000L) != 0) return 3;
    if((value & 0x80000000L) != 0) return 4;
    if((value & 0x800000) != 0) return 5;
    if((value & 0x8000) != 0) return 6;
    if((value & 0x80) != 0) return 7;
    return -1;
}

C++20添加了
countr\u zero
popcount
哪一个是最好的解决方案(如果可能的话)基本上
msb(value)/8
其中
msb
是最高有效位的位置:,C++20添加了
countr\u zero
popcount
哪一个是最好的解决方案(如果可能的话)基本上
msb(value)/8
其中
msb
是最高有效位的位置:,byteIndexOflowstSetB它正是我所需要的,而且看起来非常快。非常感谢!这正是我所需要的,而且看起来很快。非常感谢!