合并两个位掩码并解决冲突,任何两个设置位之间有一些所需的距离

Posted

技术标签:

【中文标题】合并两个位掩码并解决冲突,任何两个设置位之间有一些所需的距离【英文标题】:Merge two bitmask with conflict resolving, with some required distance between any two set bits 【发布时间】:2022-01-13 08:28:11 【问题描述】:

我有两个整数值: d_a = 6d_b = 3,所谓设置位之间的距离。 使用适当距离创建的蒙版如下所示:

uint64_t a = 0x1041041041041041; // 0001 0000 0100 0001 0000 0100 0001 0000
                                // 0100 0001 0000 0100 0001 0000 0100 0001

uint64_t b = 0x9249249249249249; // 1001 0010 0100 1001 0010 0100 1001 0010
                                 // 0100 1001 0010 0100 1001 0010 0100 1001

目标是有一个target 掩码,它的位设置为d_b,但同时考虑到a 掩码中设置的位(例如,第一个设置的位被移位)。

第二件事是target 掩码中的距离不是恒定的,即target 掩码中设置位之间的零数量应等于d_b,或者只要它们之间设置@987654331 中的位就增加@

uint64_t target = 0x4488912224488912; // 0100 0100 1000 1000 1001 0001 0010 0010
                                      // 0010 0100 0100 1000 1000 1001 0001 0010

可视化问题的图片:

蓝色条是a,黄色条是b。 我宁愿使用位操作内在函数而不是逐位操作。

编辑: 实际上,我有以下代码,但我正在寻找指令数量较少的解决方案。

void set_target_mask(int32_t d_a, int32_t d_b, int32_t n_bits_to_set, uint8_t* target)

    constexpr int32_t n_bit_byte = std::numeric_limits<uint8_t>::digits;

    int32_t null_cnt = -1;

    int32_t n_set_bit = 0;
    int32_t pos = 0;
    while(n_set_bit != n_bits_to_set)
    
        int32_t byte_idx = pos / n_bit_byte;
        int32_t bit_idx = pos % n_bit_byte;

        if(pos % d_a == 0)
        
            pos++;
            continue;
        
        null_cnt++;

        if(null_cnt % d_b == 0)
        
            target[byte_idx] |= 1 << bit_idx;
            n_set_bit++;
        
        pos++;
    

【问题讨论】:

您能解释一下如何将targetab 结合起来吗?我不确定我是否能听从你的解释。 我的目标是把它们结合起来。假设a表示已经预留资源:``` uint64 available = 0xFFFFFFFFFFFFFFFF ^ a ```现在我需要使用available根据d_b创建target 但是组合规则是什么?为什么你不能,比如说,OR 在他们之间? 标量 OR 或简单的左移或右移没有内在函数,因为它们不是必需的:|&lt;&lt; on uint64_t 已经表达了与整数相同的行为或者会。除非您的意思是要一次对多个 uint64_t 元素执行此操作,使用 _mm_or_si128 或其他什么?仍然不清楚如何解决冲突,尽管您可以使用(x &amp; (x&lt;&lt;1)) == 0 检测它们,等等,直到d_b 的班次计数。或者像x - (x&gt;&gt;d_b)(x&lt;&lt;d_b) - x 这样的东西可以在每个地区获得口罩? (未经测试,不确定是否完全有效) 我认为pdep 方法也适合数组处理,因为设置为uint64_t 的总位可以由popcnt 确定,并且可以通过左移调整掩码, 【参考方案1】:

如果目标是uint64_t,则可以通过查找表将d_ad_b 转换为位掩码。喜欢你的问题中的lut[6] == 0x2604D5C99A01041

可以在初始化期间或在编译时使用宏或常量表达式 (constexpr) 对每个程序运行初始化一次查找表。

要使d_b 展开,跳过d_a 位,您可以将pdep 与倒置的d_a 一起使用:

 uint64_t tmp = _pdep_u64(d_b_bits, ~d_a_bits);

然后您可以将n_bits_to_set 转换为连续位掩码:

 uint64_t n_bits = (1 << n_bits_to_set) - 1;

并再次使用pdep 传播它们:

 uint64_t tmp = _pdep_u64(n_bits, tmp);

(请参阅Intrinsic Guide 关于 pdep。请注意,pdep 在 Zen3 之前的 AMD 上速度较慢。在 Intel CPU 和 Zen3 上速度较快,但在 Bulldozer-family 或 Zen1/Zen2 上速度较慢)

【讨论】:

如果您知道不会发生冲突,您可以使用_bzhi_u64(tmp, n_bits_to_set * d_b) 或类似设置的位数限制将该位位置归零或更高。 (即计算位散射将在哪里结束)。但是由于两个 LUT 结果之间的交互,一些位未设置,是的,另一个 pdep 可能是有意义的。 它接近预期的结果,但我需要传播d_b,并将移位应用于冲突的d_a 位,而不是清除它们。我尝试使用 BMI2 指令但没有结果 @temteremte,第一个 pdep 的地址不正是这个:tmp = _pdep_u64(d_b_bits, ~d_a_bits); 吗? tmp 应该包含传播 d_b,不是吗? 是的,我错误地切换了参数的顺序。这正是我所需要的。谢谢!

以上是关于合并两个位掩码并解决冲突,任何两个设置位之间有一些所需的距离的主要内容,如果未能解决你的问题,请参考以下文章

根据位掩码拆分多个字节

位掩码和 Air724 LTE 模块

位运算计算位掩码再枚举——318. 最大单词长度乘积

C中的位掩码

选择位掩码中与选择器位图中的 1 位重叠的设置位范围

未检测到 SkSpriteNode 冲突