如何在 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 << n) - 1) << b
@pnezis 我认为您的功能因边缘情况而失败。如果我想得到前两位,createMask
给我第二位和第三位,因为它会做1 << 1
,然后是1 << 2
。返回的位掩码是110
- 将第一位清零。
@Jack 如果你想要前两位,那么你必须使用 0 和 1 作为参数调用createMask
,才能获得0x03
掩码。
这更快,对我也有用uint64_t
's:((1ull << (b - a)) - 1ull) << a
【参考方案3】:
Modulus 用于获取底部位(仅),尽管我认为 value & 0x1ffff
比 value % 131072
更直接地表达“获取底部 17 位”,因此这样做更容易理解。
32 位无符号值的前 17 位将是 value & 0xffff8000
(如果您希望它们仍然在顶部的位置),或者如果您希望值的前 17 位在底部,则为 value >> 15
结果的 17 位。
【讨论】:
对于答案的第二部分,您可能应该有value >> 15 & 0x1ffff
。整数的右移是实现定义的; GCC 和 MSVC 都有它的算术,而不是逻辑。
@Joseph:问题说它是一个32位无符号整数,所以右移的结果是由标准定义的。如果已签名,则右移对负值的影响(如您所说)完全由实现定义。它不需要是算术或逻辑的,结果可以是任何东西。在实践中,当然每个人都会为有符号值选择算术移位,但如果您担心写入标准,那么最好完全避免它(就像提问者那样)。【参考方案4】:
在 Intel 和 AMD CPU 上只有一条 BEXTR (Bit field extract (with register)) x86 指令,在 ARM 上只有一条 UBFX
。 _bextr_u32()
(链接需要登录)等内部函数允许显式调用此指令。
它们实现(source >> offset) & ((1 << 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 << 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;如果您需要从较大的整数“内部”提取整数,这将很有用。如果不需要,您可以省略右移 (>>
)。
【讨论】:
【参考方案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 位无符号整数?