【问题标题】:Index of first byte having its MSB set设置了 MSB 的第一个字节的索引
【发布时间】:2020-12-19 09:05:40
【问题描述】:

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

MSB 10000000 00000000 10000000 ... 10000000 00000000 00000000 LSB

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

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

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

实现是在 C++ 中。我不能使用任何内在函数或内联程序集(_BitScanForward64()、__builtin_clzll 等)。

【问题讨论】:

标签: c++ bit-manipulation


【解决方案1】:

(编辑) 隔离最低设置位x &= (-x),然后查看How to find the position of the only-set-bit in a 64-bit value using bit manipulation efficiently?,它正在检查这个确切的问题(尽管有标题)。

下面的答案稍微笼统一些。


通过消除表查找,可以通过 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 的问题更加复杂,因为我们没有任何位操作技巧来隔离所需的位。在这种情况下, Extract Bits with a Single Multiplication,将它们用作表的索引。如果不允许输入值为零,则最低有效字节的值无关紧要或非零。这允许使用具有 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];
}

【讨论】:

  • ByteIndexOfLowestSetBit 正是我所需要的,而且看起来真的很快。非常感谢!
【解决方案2】:

这里有一个简单的方法。

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;
}

【讨论】:

    猜你喜欢
    • 2021-03-30
    • 1970-01-01
    • 2015-08-31
    • 2016-12-12
    • 2015-10-08
    • 2017-08-17
    • 2017-10-04
    • 2012-05-02
    • 2020-05-26
    相关资源
    最近更新 更多