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

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(使用前将#替换为@)

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

  • 将零填充到二进制数中特定位置的命令?

    我需要将零填充到二进制数的特定位置 循环二进制数的数组形式 例如dec2bin 43 添加零并调整大小听起来像是轮子的重新发明 如何在Matlab中有效地将零填充到二进制数 Looping positions 1 3 6 x de2bi 4
  • 有条件地使用按位运算符

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

    在大神的密码库里 找到了这个函数ConstantTimeByteEq http golang org src pkg crypto subtle constant time go s 897 936 L17 它有什么作用 如何工作 Cons
  • 删除最低位

    给定一个二进制数 删除最低位的最快方法是什么 01001001010 gt 01001001000 它将在代码中用于迭代变量的位 伪代码如下 while bits 0 index getIndexOfLowestOrderBit bits
  • 如何快速将 Int16 转换为两个 UInt8 字节

    我有一些二进制数据 将两个字节值编码为有符号整数 bytes 1 255 0xFF bytes 2 251 0xF1 Decoding 这相当简单 我可以提取一个Int16这些字节的值 Int16 bytes 1 lt lt 8 Int16
  • 设置字节中的特定位

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

    我是 SSE 编程新手 所以我希望有人可以帮助我 我最近使用 GCC SSE 内在函数实现了一个函数来计算 32 位整数数组的总和 下面给出了我的实现代码 int ssum const int d unsigned int len stat
  • 除法和乘法 2 的幂

    我在一篇论文中读到 数字除以 2 的幂并乘以 2 的幂是一个微不足道的过程 我在互联网上搜索了很多解释 但没有得到它 任何人都可以用简单的语言解释一下这实际上意味着什么 从位操作的角度来看 这是微不足道的 乘以2相当于左移1位 除法相当于右
  • 使用位操作查找最小值

    任何人都可以向我解释以下代码行 它用于查找两个数字中的最小值 int min int x int y return y x y x y gt gt sizeof int CHAR BIT 1 提前致谢 它用于查找两个数字中的最小值 不幸的是
  • 如何在 C 中创建最低有效位设置为 1 的掩码

    这个功能如何运作 最低有效 n 位设置为 1 的掩码 Example n 6 gt 0x2F n 17 gt 0x1FFFF 我根本不明白这些 尤其是 n 6 gt 0x2F 另外 什么是面膜 通常的方法是采取1 并将其左移n位 这会给你类
  • 在设置/重置位方面,“分支”意味着什么?

    在一次采访中 我被问到 你如何设置或重置一点 这是一个很简单的问题 我也回答了 之后 他们问我如何做同样的事情 但不分支 我不知道什么是分支 我搜索并发现位摆弄黑客 http graphics stanford edu 7Eseander
  • 如何在 AVX/AVX2 中递增向量

    我想使用内在函数来增加 SIMD 向量的元素 最简单的方法似乎是为每个元素加 1 如下所示 note vec inc之前已设置为1 vec mm256 add epi16 vec vec inc 但是是否有任何特殊指令来增加向量 类似于in
  • 优化 tribool 数组的空间

    让我从一些背景开始 通过 tribool 我理解一个可以保存以下值之一的变量 true false or null 有问题复制整数数组与布尔指针数组 https stackoverflow com questions 4350041 cop
  • 不使用“-”运算符将两个数字相减

    我尝试使用以下代码 但我不明白为什么它给了我错误的答案 我正在计算 2 的补码并添加另一个数字 include
  • 负整数的Python表示

    gt gt gt x 4 gt gt gt print b format x x 4 100 gt gt gt mask 0xFFFFFFFF gt gt gt print b format x mask x mask 4294967292
  • 将 4 个字节转换为无符号 32 位整数并将其存储在 long 中

    我正在尝试用 Java 读取二进制文件 我需要读取无符号 8 位值 无符号 16 位值和无符号 32 位值的方法 执行此操作的最佳 最快 最美观的代码 是什么 我在 C 中做到了这一点 并做了类似的事情 uint8 t buffer uin
  • 添加饱和 32 位有符号整数内在函数?

    有人可以推荐一种使用 Intel 内在函数 AVX SSE4 添加饱和 32 位有符号整数的快速方法吗 我查看了内在指南并发现 mm256 adds epi16但这似乎只添加 16 位整数 我没有看到 32 位有任何类似的东西 其他电话似乎
  • 如何使用 C 替换位域中的位而不影响其他位

    我想替换 32 64 位数据字段中的一位 位 多个位 而不影响其他位 举例来说 我有一个 64 位寄存器 其中第 5 位和第 6 位可以取值 0 1 2 和 3 5 6 0 0 0 1 1 0 1 1 现在 当我读取寄存器时 我得到的值是
  • 两个 16 位数字相乘 - 为什么结果是 32 位长? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 如果我将两个 16 位数字相乘 结果将是 32 位长 但为什么会这样呢 对此有何明确解释 为了我的正确理解 其计算方法是 n 位数字乘以
  • 在第 i 个位置切换一点[重复]

    这个问题在这里已经有答案了 可能的重复 如何在 C 中设置 清除和切换单个位 https stackoverflow com questions 47981 how do you set clear and toggle a single

随机推荐

  • 使用 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