如何在 C 中提取 32 位无符号整数的特定“n”位?

Posted

技术标签:

【中文标题】如何在 C 中提取 32 位无符号整数的特定“n”位?【英文标题】:How do I extract specific 'n' bits of a 32-bit unsigned integer in C? 【发布时间】:2011-11-04 15:28:09 【问题描述】:

谁能告诉我如何从 C 中的 32 位无符号整数中提取“n”个特定位。

例如,假设我想要 32 位值的前 17 位;我该怎么办? 我想我应该使用模数运算符,我试过了,能够得到最后 8 位和最后 16 位

unsigned last8bitsvalue=(32 bit integer) % 16
unsigned last16bitsvalue=(32 bit integer) % 32

这是正确的吗?有没有更好更有效的方法来做到这一点?

【问题讨论】:

请注意,您编写的代码实际上分别提取了最后 4 位和 5 位(不是您所写的 8 位和 16 位)。 @AaronDufour 你能解释一下你的评论吗?我对为什么是 4 位和 5 位感兴趣 @CholthiPaulTtiopic 您可以使用x % (2 ^ n)x % 16 -> x % 2^4 -> 4 位,类似地x % 32 -> 5 位)获取数字 x 的最后 n 位。如果你这样做x % n,那么你会发现你的输出范围仅从 0 到 n-1,而 n 位可以包含数字 0 到 2^n-1。 @AaronDufour 精彩的解释! 【参考方案1】:

与其将其视为“提取”,我更愿意将其视为“隔离”。一旦所需的位被隔离,您就可以对它们做任何您想做的事情。

要隔离任何一组位,请应用 AND 掩码。

如果你想要一个值的最后 X 位,可以使用一个简单的技巧。

unsigned  mask;
mask = (1 << X) - 1;
lastXbits = value & mask;

如果你想在 'startBit' 开始的 'value' 中间隔离一系列 X 位 ...

unsigned  mask;
mask = ((1 << X) - 1) << startBit;
isolatedXbits = value & mask;

希望这会有所帮助。

【讨论】:

您强调首先制作Mask字节对解决此类问题非常有帮助 需要更多解释屏蔽部分。没有得到太多 @user765443:假设你想从 0101 中提取最后 3 位,1 此方法不起作用,如果 X = 32 表示 int(或 64 表示 long) @knst - 你是对的;在您引用的条件下,移位时我们会遇到未定义的行为。因此,取决于处理器,它可能工作(或不工作);换句话说,对于这些情况,它是不可移植的。还可以观察到,如果您尝试提取或隔离所有位,只需像往常一样访问变量,不要费心应用任何不必要的掩码。【参考方案2】:

如果你想要特定的 n 位,那么你可以先创建一个位掩码,然后用你的号码 AND 它来获取所需的位。

从位 a 到位 b 创建掩码的简单函数。

unsigned createMask(unsigned a, unsigned b)

   unsigned r = 0;
   for (unsigned i=a; i<=b; i++)
       r |= 1 << i;

   return r;

您应该检查 a

如果您希望位 12 到 16 调用该函数,然后简单地 &(逻辑与)r 与您的号码 N

r = createMask(12,16);
unsigned result = r & N;

如果你愿意,你可以改变结果。希望这会有所帮助

【讨论】:

无需花时间循环寻找小面具。只是 ((1 &lt;&lt; n) - 1) &lt;&lt; b @pnezis 我认为您的功能因边缘情况而失败。如果我想得到前两位,createMask 给我第二位和第三位,因为它会做1 &lt;&lt; 1,然后是1 &lt;&lt; 2。返回的位掩码是110 - 将第一位清零。 @Jack 如果你想要前两位,那么你必须使用 0 和 1 作为参数调用createMask,才能获得0x03 掩码。 这更快,对我也有用uint64_t's:((1ull &lt;&lt; (b - a)) - 1ull) &lt;&lt; a【参考方案3】:

Modulus 用于获取底部位(仅),尽管我认为 value &amp; 0x1ffffvalue % 131072 更直接地表达“获取底部 17 位”,因此这样做更容易理解。

32 位无符号值的前 17 位将是 value &amp; 0xffff8000(如果您希望它们仍然在顶部的位置),或者如果您希望值的前 17 位在底部,则为 value &gt;&gt; 15结果的 17 位。

【讨论】:

对于答案的第二部分,您可能应该有value &gt;&gt; 15 &amp; 0x1ffff。整数的右移是实现定义的; GCC 和 MSVC 都有它的算术,而不是逻辑。 @Joseph:问题说它是一个32位无符号整数,所以右移的结果是由标准定义的。如果已签名,则右移对负值的影响(如您所说)完全由实现定义。它不需要是算术或逻辑的,结果可以是任何东西。在实践中,当然每个人都会为有符号值选择算术移位,但如果您担心写入标准,那么最好完全避免它(就像提问者那样)。【参考方案4】:

在 Intel 和 AMD CPU 上只有一条 BEXTR (Bit field extract (with register)) x86 指令,在 ARM 上只有一条 UBFX_bextr_u32()(链接需要登录)等内部函数允许显式调用此指令。

它们实现(source &gt;&gt; offset) &amp; ((1 &lt;&lt; n) - 1) C 代码:从source 中获取n 连续位,从offset 位开始。这是一个处理边缘情况的完整函数定义:

#include <limits.h>

unsigned getbits(unsigned value, unsigned offset, unsigned n)

  const unsigned max_n = CHAR_BIT * sizeof(unsigned);
  if (offset >= max_n)
    return 0; /* value is padded with infinite zeros on the left */
  value >>= offset; /* drop offset bits */
  if (n >= max_n)
    return value; /* all  bits requested */
  const unsigned mask = (1u << n) - 1; /* n '1's */
  return value & mask;

例如,要从2273 (0b100011100001) 中以5-th 位开始获取3 位,请调用getbits(2273, 5, 3)——它会提取7 (0b111)。

例如,假设我想要 32 位值的前 17 位;我该怎么办?

unsigned first_bits = value & ((1u << 17) - 1); // & 0x1ffff

假设CHAR_BIT * sizeof(unsigned) 在您的系统上是 32。

我认为我应该使用模数运算符,我尝试了它并且能够获得最后 8 位和最后 16 位

unsigned last8bitsvalue  = value & ((1u <<  8) - 1); // & 0xff
unsigned last16bitsvalue = value & ((1u << 16) - 1); // & 0xffff

如果在问题中的所有示例中偏移量始终为零,那么您不需要更通用的getbits()。有一个特殊的 cpu 指令 BLSMSK 可以帮助compute the mask ((1 &lt;&lt; n) - 1)

【讨论】:

【参考方案5】:

如果您需要整数的最后 X 位,请使用 binary mask :

unsigned last8bitsvalue=(32 bit integer) & 0xFF
unsigned last16bitsvalue=(32 bit integer) & 0xFFFF

【讨论】:

【参考方案6】:

这是已接受答案的更简短的变体:下面的函数通过创建位掩码来提取从到包含的位。在对原始数字应用 AND 逻辑后,结果被移位,因此函数只返回提取的位。 为了清楚起见,跳过了索引/完整性检查。

uint16_t extractInt(uint16_t orig16BitWord, unsigned from, unsigned to) 

  unsigned mask = ( (1<<(to-from+1))-1) << from;
  return (orig16BitWord & mask) >> from;

【讨论】:

这似乎也适用于 uint32_t 输入和返回,而不是 uint16_t。【参考方案7】:

按位与您的整数与掩码正好具有您想要提取的那些位集。如果需要,然后将结果右移以重新定位提取的位。

unsigned int lowest_17_bits = myuint32 & 0x1FFFF;
unsigned int highest_17_bits = (myuint32 & (0x1FFFF << (32 - 17))) >> (32 - 17);

编辑:后者将最高 17 位重新定位为最低 17;如果您需要从较大的整数“内部”提取整数,这将很有用。如果不需要,您可以省略右移 (&gt;&gt;)。

【讨论】:

【参考方案8】:
#define GENERAL__GET_BITS_FROM_U8(source,lsb,msb) \
    ((uint8_t)((source) & \
        ((uint8_t)(((uint8_t)(0xFF >> ((uint8_t)(7-((uint8_t)(msb) & 7))))) & \
             ((uint8_t)(0xFF << ((uint8_t)(lsb) & 7)))))))

#define GENERAL__GET_BITS_FROM_U16(source,lsb,msb) \
    ((uint16_t)((source) & \
        ((uint16_t)(((uint16_t)(0xFFFF >> ((uint8_t)(15-((uint8_t)(msb) & 15))))) & \
            ((uint16_t)(0xFFFF << ((uint8_t)(lsb) & 15)))))))

#define GENERAL__GET_BITS_FROM_U32(source,lsb,msb) \
    ((uint32_t)((source) & \
        ((uint32_t)(((uint32_t)(0xFFFFFFFF >> ((uint8_t)(31-((uint8_t)(msb) & 31))))) & \
            ((uint32_t)(0xFFFFFFFF << ((uint8_t)(lsb) & 31)))))))

【讨论】:

也许不是最美观的方式,但使用 define 可能会很有趣。【参考方案9】:
int get_nbits(int num, int n)

return  (((1<<n)-1) & num);

【讨论】:

您应该添加一些关于此代码如何工作的解释(如果确实有效)。 这甚至没有正确的签名来处理unsigned int。您还需要将1 更正为1u

以上是关于如何在 C 中提取 32 位无符号整数的特定“n”位?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 AVX2 中将 32 位无符号整数转换为 16 位无符号整数?

如何使用 sprintf 显示 64 位无符号整数?

使用 32 位无符号整数乘以 64 位数字的算法

如何从 16 x 8 位 __m128i 值中提取 32 x 4 位整数

Shopee 一面算法题:颠倒给定的 32 位无符号整数

如何将两个32位整数组合成一个64位整数?