当前位置: 首页 > 知识库问答 >
问题:

如何取消设置 N 个最右边的设置位

王飞英
2023-03-14

有一个相对著名的技巧来取消设置单个最右边的位:

y = x & (x - 1) // 0b001011100 & 0b001011011 = 0b001011000 :)

我发现自己有一个严密的循环来清除n个最右边的位,但有更简单的代数技巧吗?

假设相对较大的n(n必须为

// x = 0b001011100 n=2
for (auto i=0; i<n; i++) x &= x - 1;
// x = 0b001010000

我已经用大拇指敲了几次我的TAOCP Vol4,但找不到任何灵感。

也许有一些硬件支持?

共有1个答案

古彦
2023-03-14

对于具有 BMI2 的英特尔 x86 CPU,pextpdep 速度很快。Zen3之前的AMD具有非常慢的微编码PEXT/PDEP(https://uops.info/),所以要小心;其他选项在AMD上可能更快,甚至可能是循环中的blsi,或者在popcount上更好地进行二进制搜索(见下文)。
只有英特尔有专用的硬件执行单元,用于 pext/pdep 执行的掩码控制打包/解包,使其时间恒定:1 uop,3 周期延迟,只能在端口 1 上运行。

我不知道其他isa有类似的位打包硬件操作。

pdep基础知识:pdep(-1ULL, a)==a。从第一个操作数中获取低popcnt(a)位,并将它们存放在a设置位的位置,将再次返回a

但是,如果您的位源不是全1,而是清除了低N位,则a中的前N个设置位将获取0而不是1。这正是您想要的。

uint64_t unset_first_n_bits_bmi2(uint64_t a, int n){
    return _pdep_u64(-1ULL << n, a);
}

<代码>-1ULL

在 Godbolt 上使用一个简单的循环参考实现,表明它们为示例输入 an 产生相同的结果。

GCC和clang都以显而易见的方式编译它,如下所写:

# GCC10.2 -O3 -march=skylake
unset_first_n_bits_bmi2(unsigned long, int):
        mov     rax, -1
        shlx    rax, rax, rsi
        pdep    rax, rax, rdi
        ret

(SHLX 是单 uop,1 个周期延迟,与更新 FLAG 的传统变量计数移位不同......除非 CL=0)

所以这有3个周期延迟从a-

前端只有3个uop。

一个半相关的BMI2技巧:

pext(a, a)将打包底部的位,如(1ULL

用AND掩码清除其中的低N位,并用pdep展开即可。但这是一种过于复杂且昂贵的方法来创建一个比特源,其中有N个零以上的比特,这对pdep来说才是真正重要的。感谢@harold在这个答案的第一个版本中发现了这一点。

@Nate建议对要清除多少低位进行二进制搜索可能是 pdep 的一个很好的替代方案。

当<code>popcount(x

完成搜索后,x

性能popcount在现代CPU上相对广泛可用。(虽然不是x86-64的基线;您仍然需要使用-mpopcnt-march=native进行编译)。

调整这一点可能需要选择一个可能的起点,并可能使用最大初始步长,而不是纯二进制搜索。从尝试一些初始猜测中获得一些指令级并行性可能有助于缩短延迟瓶颈。

 类似资料:
  • 问题内容: 如何使用jQuery设置和取消设置Cookie,例如创建一个名为的Cookie 并将其值设置为? 问题答案: 2019年4月更新 Cookie的读取/操作不需要jQuery,因此请不要使用下面的原始答案。 转到https://github.com/js-cookie/js-cookie,然后在其中使用不依赖jQuery的库。 基本示例: 有关详细信息,请参见github上的文档。 参见

  • 问题内容: 如何在Java中的long的特定位置设置/取消设置位? 例如, 我想在位置2设置位,在位置3取消设置位,因此相应的long将是, 有人可以帮我怎么做吗? 问题答案: 要设置一点,请使用: 擦除一下使用: 切换一下用途: 请注意,我使用0b?。您也可以使用任何整数,例如: 但是,这使得更难知道正在更改哪个位。 使用二进制可让您查看将要设置/擦除/切换的确切位。 要动态设置位,请使用: 将

  • 问题内容: 我在JavaScript中有一个全局变量(实际上是一个属性,但我认为它并不重要),该变量已经由先前的脚本填充,但是我不希望另一个脚本稍后运行以查看其值,甚至定义。 我已经说过了,它可以用于测试目的,但是我真的不认为这是正确的方法。 你怎么看? 问题答案: 操作者,将删除该对象的属性。它不能删除变量。因此,问题的答案取决于如何定义全局变量或属性。 (1)如果使用创建,则无法删除。 例如:

  • 本文向大家介绍如何设置字体的左右间距?相关面试题,主要包含被问及如何设置字体的左右间距?时的应答技巧和注意事项,需要的朋友参考一下

  • 我希望能够在Android上使用标签技术在MIFARE Ultralight EV1(MFOUL21)标签上设置和取消设置密码保护。 我知道我会使用<code>nfcA。tranceive()方法,但我不确定该方法的参数是什么,所以有人可以提供代码段来设置和取消设置密码吗? 更新: 关于 TapLinx 库,我基本上希望 代码片段等同于:

  • 本文向大家介绍如何取消同级li的最后一个li标签的右边距?相关面试题,主要包含被问及如何取消同级li的最后一个li标签的右边距?时的应答技巧和注意事项,需要的朋友参考一下 可以先总体设置右边距,然后再单独取消最后一个li元素的右边距 也可以直接给除了最后一个li以外的元素设置右边距

  • 我正在使用Kafka(与雅虎Kafka经理) 我想为重置消息设置一个规则,或者他们如何称呼它:“分区偏移量的总和” 在server.properties上是否有滚动kafka偏移量的参数? (即:我想重置或删除所有影响邮件保留的参数) 谢谢。

  • 脚本需要jQuery 一切正常,但我需要能够改变下拉菜单的颜色,当它被选项框选中时,变为(亮绿色)。如果选择了另一个选项,则再次变灰。 我已经尝试了我读到的所有建议的提示,即. $( "#mOptions"). prop(. css('backder-Color','#ffffff'));任何颜色。请帮助,非常感谢。没有用。 脚本 表格: