如何取消设置和最右边的设置位

2023-12-08

有一个相对知名的技巧可以取消设置最右边的一个位:

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

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

假设 n 相对较大(对于 64 位整数,n 必须小于 64,但通常约为 20-30)。

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

我翻阅了 TAOCP Vol4 几次,但找不到任何灵感。

也许有一些硬件支持?


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

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


pdep basics: pdep(-1ULL, a) == a。从第一个操作数中取出低 popcnt(a) 位,并将它们存放在a已设置位,会给你a再次回来。

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

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

-1ULL << n适用于 C 中的 n=0..63。 x86 asm 标量移位指令屏蔽了它们的计数(有效地&63), 所以那是probably更大的 C 未定义行为会发生什么n。如果您关心,请使用n&63在源代码中,因此行为在 C 中定义良好,并且它仍然可以编译为直接使用计数的移位指令。

关于上帝之锤使用简单的循环参考实现,表明它们对样本输入产生相同的结果a and n.

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 是单微操作,1 个周期延迟,与更新 FLAGS 的传统变量计数移位不同......除非 CL=0)

所以这有 3 个周期延迟a->输出(只是pdep)
和 4 个周期延迟n->输出(shlx,pdep)。

对于前端来说只有 3 uop。


一个半相关的 BMI2 技巧:

pext(a,a)将把这些位打包在底部, like (1ULL<<popcnt(a)) - 1但如果所有位均已设置,则不会溢出。

用 AND 掩码清除该值的低 N 位,并用pdep会工作。但这是一种过于复杂且昂贵的方式来创建具有足够多的高于 N 个零的位源,而这对 pdep 来说才是真正重要的。感谢@harold 在这个答案的第一个版本中发现了这一点。


没有快速 PDEP:也许可以通过二分搜索来找到正确的 popcount

@Nate 的建议二分查找要清除多少个低位可能是 pdep 的一个很好的替代品。

停止时popcount(x>>c) == popcount(x) - N找出要清除的低位,最好使用无分支更新c。 (例如。c = foo ? a : b通常编译为 cmov)。

一旦你完成搜索,x & (-1ULL<<c)使用该计数,或者只是tmp << c移回x>>c结果你已经有了。直接使用右移比生成新掩码并在每次迭代中使用它更便宜。

高性能 popcount 在现代 CPU 上相对广泛地可用。 (虽然notx86-64 的基线;你仍然需要编译-mpopcnt or -march=native).

对此进行调整可能涉及选择一个可能的起点,并且可能使用最大初始步长而不是纯二分搜索。通过尝试一些初步猜测来获得一些指令级并行性可能有助于缩短延迟瓶颈。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何取消设置和最右边的设置位 的相关文章

  • 如何从 Swift 中的整数获取特定位?

    尝试将我的应用程序从 C 转换为 Swift C static QWORD load64 const OCTET x char i QWORD u 0 for i 7 i gt 0 i u lt lt 8 u x i return u Sw
  • Python 中的按位运算是如何进行的?

    我今天一直在学习按位运算 我了解到Not 反转所有位 例如 01010 to 10101 这意味着 10应该是 5 但我看到它是 11 每个python命令行 这是 01010 to 11011 只有两位被反转 谁能解释一下为什么不是101
  • 按位 - 如何检查一个二进制数是否包含另一个二进制数?

    A 110000000 384 Blue Red B 011000010 194 Green Black Red A B C 010000000 128 Red 我如何检查 B 是否包含 A 中的所有位以及其他位 在上面的情况下 我想得到
  • 整数除以 3 最快的方法是什么?

    int x n 3 lt make this faster for instance int a n 3 lt normal integer multiplication int b n lt lt 1 n lt potentially f
  • 使用按位运算符计算两个数字的和

    我粘贴代码以使用按位运算符查找两个数字的总和 请建议是否可以优化 谢谢 public static int getSum int p int q int carry 0 result 0 for int i 0 i lt 32 i int
  • 如何仅使用按位运算符实现 Bitcount?

    任务是仅使用按位运算符实现位计数逻辑 我让它工作得很好 但我想知道是否有人可以建议一种更优雅的方法 仅允许按位运算 没有 如果 因为 等 int x 4 printf d n x 0x1 printf d n x gt gt 1 0x1 p
  • 如何更改32位寄存器的特定位而不更改其他位?

    我想直接使用寄存器的物理地址来操作寄存器的某些位 但是我找不到方法来做到这一点 我看到一些关于设置位掩码的帖子 但我发现它们太令人困惑了 我的寄存器物理地址是 0x4A10005C 我想操纵它的 18 16 位之间的位 我想设置0x3在那些
  • 按位或(在数组上)

    我需要在 Java 中对两个字节数组执行按位或运算 我怎样才能这样做呢 byte a new byte 256 byte b new byte 256 byte c it should contain information i e bit
  • Postgres 中的按位运算

    我有以下表格 types id name 1 A 2 B 4 C 8 D 16 E 32 F and vendors id name type 1 Alex 2 type B only 2 Bob 5 A C 3 Cheryl 32 F 4
  • C# 中反转 1 位

    我有 1 位byte 始终处于最低顺序位置 我想反转 即给定 00000001 我想要得到 00000000 而对于 00000000 我想要 00000001 我是这样解决的 bit gt 0 0 1 我很好奇还能怎么做 怎么样 bit
  • 什么时候右移操作>>移位符号位什么时候不呢?

    我的问题是为什么a gt gt 1移位符号位 但不移位 a 0xaaaaaaaa gt gt 1 代码片段 int a 0xaaaaaaaa std cout lt lt sizeof a lt lt std endl getBits a
  • 将位的字符串表示形式转换为字节

    我刚刚开始学习文件压缩 但遇到了一些障碍 我有一个应用程序将诸如 程序 之类的字符串编码为压缩的二进制表示形式 010100111111011000 请注意 这仍然存储为字符串 Encoding g 111 r 10 a 110 p 010
  • 如何理解Python中的i和-i? python 中的位操作

    我发现Python中的 表示基于位表达式的 与 运算 最近 我发现了一个非常聪明的代码 其中一行类似于 i i 其中 i 是一个整数 如何理解 i i 的结果 另外 python如何处理负整数 i 进行位操作 i i 清除所有位 1 但最后
  • 如果可能的话,如何在 C 中定义 2 位数字?

    对于我的大学过程 我正在模拟一个称为随机顺序吸附的过程 我必须做的一件事是随机地将正方形 不能重叠 放置到格子上 直到没有更多空间为止 重复该过程几次以找到平均 干扰 覆盖率 基本上我正在对一个大的整数数组执行操作 其中存在 3 个可能的值
  • AVX 中的分散内在函数

    我在 Intel Intrinsic Guide v2 7 中找不到它们 您知道 AVX 或 AVX2 指令集是否支持它们吗 原始AVX指令集中没有分散或聚集指令 AVX2 添加了聚集指令 但没有添加分散指令 AVX512F 包括分散和聚集
  • SSE、内在函数和对齐

    我使用大量 SSE 编译器内在函数编写了一个 3D 矢量类 一切都工作正常 直到我开始使用 new 来实例化具有 3D 向量作为成员的类 我在发布模式下经历了奇怪的崩溃 但在调试模式下却没有 反之亦然 因此 我阅读了一些文章 并认为我需要将
  • 有条件地使用按位运算符

    条件运算符如何使用按位运算符表示 这是一个家庭作业问题 我必须仅使用按位运算来实现条件运算符 那就很简单了 如果if允许使用语句 但它必须是严格的按位运算符 仅运营商 gt gt and lt lt 可以使用 不if可以使用语句或循环 该函
  • 在 C 中实现逻辑右移

    我正在致力于仅使用按位运算符在 C 中创建逻辑右移函数 这是我所拥有的 int logical right shift int x int n int size sizeof int size of int arithmetic shift
  • 在 C++ 中将 64 位值左移 64 位给出奇怪的结果[重复]

    这个问题在这里已经有答案了 可能的重复 64位移位问题 https stackoverflow com questions 1024968 64bit shift problem 我在 Windows 8 64 位上使用 Visual St
  • 设置字节中的特定位

    我正在尝试设置 Java 字节变量中的位 它确实提供了适当的方法 例如 setBit i 有谁知道我如何才能实现这一点 我可以按位迭代给定的字节 if my byte 1 lt lt i 0 但是我不能将此位置设置为 1 或 0 可以吗 使

随机推荐

  • 使用 na.approx 在数据框中插入 NA 值

    我正在尝试删除NA通过插值从我的数据框中获取na approx 但无法删除所有NAs 我的数据帧是 4096x4096 其中 270 15 作为无效值的标志 我需要在所有点上连续的数据来提供气象模型 昨天我询问并获得了关于如何基于另一个数据
  • 循环创建PyQt5按钮:所有按钮触发相同的回调

    我应该提到 我已经阅读了这些内容 但我仍然无法实现我的目标 在 for 循环中使用字典来创建按钮不起作用 循环中的 QtCore QObject connect 仅影响最后一个实例 我的目标是制作一个 Linux 启动器 应用程序 按钮的创
  • session_start() 错误

    我无法处理这个错误 请帮助我 它可以在我的笔记本电脑上运行 但不能在我的台式机上运行 Why Warning session start function session start Cannot send session cache li
  • 如何让代码在Response.end之后执行

    我的代码是这样的 HttpContext Current Response Clear HttpContext Current Response ContentType application pdf HttpContext Current
  • 使用 LocationClient 获取位置更新

    我该如何使用locationclient类与requestLocationUpdates LocationRequest LocationListener 在android中获取位置更新 我已经尝试过以下代码 但它不起作用 谁能帮我这个 哪
  • 在Sql Server中编写TRANSFORM语句

    我正在将 Web 应用程序后端从 Access 迁移到 MSSQL 但是我无法在 MSSQL 中重现以下查询 有什么想法吗 TRANSFORM First FollowUp FUData AS FirstOfFUData SELECT Fo
  • 使用 WCF 服务返回 List

    我得到了一个Employee班级和每个员工都有一份请假清单 可以给个清单吗AppliedLeave as a DataMember in WCF DataContract public class Employee DataMember p
  • Typescript:无法在模块外部使用 import 语句

    我在 Node js 2019 年 10 月 7 日最新版本的 Node js 应用程序中有一个 ts 文件 可以导入节点模块而无需默认导出 我使用这个结构 import Class from abc 当我运行代码时 出现以下错误 Cann
  • 访问 nullptr 怎么可能有效? [复制]

    这个问题在这里已经有答案了 我有一个简单的课程 class B public int getData return 3 然后 我用 nullptr 初始化指向它的指针 B foo nullptr 然后 尝试使用它会带来惊喜 int t fo
  • 转换列并更新 DataFrame

    所以 我下面要做的是删除一列A from a DataFrame因为我想应用一个转换 这里我只是json loadsJSON 字符串 并将旧列替换为转换后的列 转换后 我只需连接两个结果数据框 df df data drop A join
  • 如何比较“看起来相似”的 Unicode 字符?

    我陷入了一个令人惊讶的问题 我在应用程序中加载了一个文本文件 并且有一些逻辑来比较 的值 我意识到即使文本相同 比较值也是错误的 Console WriteLine Equals returns false Console WriteLin
  • OpenCV StereoRectify 扭曲图像

    我们有一个 ELP 1 0 百万像素双镜头 USB 立体相机 我们正在尝试使用 C 中的 OpenCV 3 1 来校准它 然而 校准的结果完全无法使用 因为调用stereoRectify完全扭曲了图像 这就是我们所做的 在两个相机中找到校准
  • 使用 Java/Socket 的简单 Http 服务器?

    我目前正在创建一个返回静态页面的小型 HTTP 服务器 p Hello p 我尝试使用 Java 的套接字 public static void main String args throws Exception cr ation de l
  • 使用 SuiteTalk 获取采购订单中的项目

    我正在尝试使用 SuiteTalk 从采购订单中获取商品和一些相关信息 我能够获得所需的采购订单TransactionSearch在 Scala 中使用以下内容 val transactionSearch new TransactionSe
  • python 字符串模块与 str 方法

    gt gt gt import string gt gt gt s happy cat gt gt gt string find s cat 6 and gt gt gt s happy cat gt gt gt s find cat 6
  • Netbeans 11.2:没有为项目或全局定义合适的部署服务器

    我在 Mac 上安装了 Netbeans 11 2 IDE 在 服务 gt 服务器 下 我添加了 GlassFish Server 作为服务器 然后我打开了一个maven项目 我可以 清理和建造 它 然后我想运行它 但这导致了以下错误消息
  • 如何将图像插入到闪亮的 navbarPage() 上的导航栏中

    我正在使用一个闪亮的应用程序navbarPage 布局 我想在屏幕右侧的导航栏中插入图像 例如 它看起来像 stackoverflow 网站顶部的导航栏 但在最右侧有一个徽标 我努力了 shinyUI navbarPage title te
  • 传递多个模型查看

    public ActionResult Index var pr db products return View pr 首先 我想传递给视图更多数据 例如 public ActionResult Index var pr db produc
  • 今天是一年中的第 n 天 [重复]

    这个问题在这里已经有答案了 我想获得天数 即 1 月 1 日是第 1 天 1 月 2 日是第 2 天 2 月 1 日是第 32 天 12 月 31 日是第 365 或 366 天 具体取决于是否闰年 我使用了各种技术 例如 date1 da
  • 如何取消设置和最右边的设置位

    有一个相对知名的技巧可以取消设置最右边的一个位 y x x 1 0b001011100 0b001011011 0b001011000 我发现自己有一个紧密的循环来清除最右边的 n 位 但是有更简单的代数技巧吗 假设 n 相对较大 对于 6