【问题标题】:How to create mask with least significat bits set to 1 in C如何在C中创建最低有效位设置为1的掩码
【发布时间】:2012-09-14 00:29:49
【问题描述】:

谁能给我解释一下这个功能?

最低有效 n 位设置为 1 的掩码。

例如:

n = 6 --> 0x2F, n = 17 --> 0x1FFFF // 我一点也不明白,尤其是 n = 6 --> 0x2F

还有,什么是面具?

【问题讨论】:

标签: c bit-manipulation bitmask


【解决方案1】:

通常的方法是取1,并将其向左移动n 位。这会给你类似的东西:00100000。然后从中减去 1,这将清除已设置的位,并设置所有较低有效位,因此在这种情况下,我们将得到:00011111

掩码通常用于按位运算,尤其是and。您将使用上面的掩码自己获取 5 个最低有效位,与可能存在的任何其他内容隔离。这在处理通常具有单个硬件寄存器的硬件时尤其常见,该寄存器包含表示许多完全独立、不相关的数量和/或标志的位。

【讨论】:

  • 请记住,去1 << w - 1,其中w 是数据类型的宽度,设置除一位以外的所有位,是UB。
  • 没错。归咎于英特尔,但它达到了标准。
【解决方案2】:

掩码是一个整数值的常用术语,它与另一个整数值进行按位与、或、异或等运算。

例如,如果您想提取一个 int 变量的 8 个最低有效数字,您可以使用variable & 0xFF。 0xFF 是一个掩码。

同样,如果你想设置位 0 和 8,你可以使用variable | 0x101,其中 0x101 是一个掩码。

或者,如果您想反转相同的位,您可以使用variable ^ 0x101,其中 0x101 是掩码。

要为您的情况生成一个掩码,您应该利用一个简单的数学事实,即如果您将 1 添加到您的掩码(掩码的所有最低有效位都设置为 1,其余位设置为 0),您会得到一个值为2的幂。

所以,如果你生成最接近 2 的幂,那么你可以从中减去 1 来得到掩码。

使用 C 中的左移 << 运算符很容易生成 2 的正幂。

因此,1 << n 产生 2n。在二进制中它是 10...0 和 n 0s。

(1 << n) - 1 将生成一个掩码,其中n 最低位设置为 1。

现在,您需要注意左移时的溢出。在 C(和 C++)中,您不能合法地将变量左移尽可能多的位位置,因此如果 int 是 32 位,1<<32 会导致undefined behavior。还应避免有符号整数溢出,因此您应该使用无符号值,例如1u << 31.

【讨论】:

    【解决方案3】:

    就正确性和性能而言,自从 2012 年由于现代 x86 处理器(特别是 BLSMSK)中 BMI 指令的出现而提出这个问题以来,实现这一目标的最佳方法已经发生了变化。

    这是解决此问题的好方法,同时保持与旧处理器的向后兼容性。

    此方法是正确的,而当前最热门的答案在极端情况下会产生未定义的行为。

    当允许使用 BMI 指令进行优化时,Clang 和 GCC 会将 gen_mask() 压缩为两个操作。使用支持硬件,请务必为 BMI 指令添加编译器标志: -mbmi -mbmi2

    #include <inttypes.h>
    #include <stdio.h>
    
    uint64_t gen_mask(const uint_fast8_t msb) {
      const uint64_t src = (uint64_t)1  << msb;
      return (src - 1) ^ src;
    }
    
    int main() {
      uint_fast8_t msb;
      for (msb = 0; msb < 64; ++msb) {
        printf("%016" PRIx64 "\n", gen_mask(msb));
      }
      return 0;
    }
    

    【讨论】:

    • 对不起,这是一个误解:我会使用宽度作为参数(就像 OP 提到的 N 一样),但由于您使用 MSB 的索引,它实际上是一致的。
    • 在这种情况下 consting 做了什么?
    • 我认为@UlrichEckhardt 的意思是,如果 msb 定义了包容性最高位(这是典型用法),那么您的掩码太短了 1 位。根据包容性定义,msb 为 1 应选择位 1 和 0,因此掩码为 0x3,但您的代码生成 0x1。 src = 1LL &lt;&lt; (msb + 1) 更好。或者——把你的变量名从“msb”改成“num_of_bits”然后你就对了。
    • 因为输入已经被移动了一个,这个解决方案适用于所有的位设置 (msb == 63),但是你不能再要求一个没有设置位的掩码,因为msb == 0 给你底部位设置。
    【解决方案4】:

    首先,对于那些只想通过代码创建掩码的人:

    uint64_t bits = 6;
    uint64_t mask = ((uint64_t)1 << bits) - 1;
    # Results in 0b111111 (or 0x03F)
    

    对于那些想知道面具是什么的人:

    掩码通常是值的名称,我们使用它来使用按位运算(例如 AND、OR、XOR 等)来操作其他值。

    短掩码通常用二进制表示,我们可以明确地看到所有设置为 1 的位。

    较长的掩码通常用十六进制表示,一旦掌握它就很容易阅读。

    您可以阅读更多关于 C here 中的按位运算的信息,以便更好地掌握材料。

    【讨论】:

      【解决方案5】:

      我相信你的第一个例子应该是0x3f

      0x3f 是数字 63 的十六进制表示法,即二进制的 111111,因此最后 6 位(最低有效 6 位)设置为 1

      以下小 C 程序将计算正确的掩码:

      #include <stdarg.h>
      #include <stdio.h>
      
      int mask_for_n_bits(int n)
      {
          int mask = 0;
      
          for (int i = 0; i < n; ++i)
              mask |= 1 << i;
      
          return mask;
      }
      
      int main (int argc, char const *argv[])
      {
          printf("6: 0x%x\n17: 0x%x\n", mask_for_n_bits(6), mask_for_n_bits(17));
          return 0;
      }
      

      【讨论】:

        【解决方案6】:

        0x2F 是二进制的0010 1111 - 这应该是0x3f,它是二进制的0011 1111,并且设置了6 个最低有效位。

        同样,0x1FFFF 是二进制的0001 1111 1111 1111 1111,它设置了 17 个最低有效位。

        “掩码”是一个值,它旨在使用按位运算符(如&amp;|^)与另一个值组合,以单独设置、取消设置、翻转或保持其他值不变价值。

        例如,如果您使用 &amp; 运算符将掩码 0x2F 与某个值 n 组合在一起,则除 6 个最低有效位之外,结果将全部为零,并且这 6 个位将从值n

        对于&amp; 掩码,掩码中的二进制0 表示“无条件地将结果位设置为0”,1 表示“将结果位设置为输入值位”。对于| 掩码,掩码中的0 将结果位设置为输入位,1 无条件地将结果位设置为1,对于^ 掩码,0 设置结果位为输入位,1 将结果位设置为输入位的补码。

        【讨论】:

        • 操作。更新后编辑错误,但我确实回滚了。对不起!
        猜你喜欢
        • 1970-01-01
        • 2013-11-13
        • 1970-01-01
        • 1970-01-01
        • 2010-10-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多