如何在C语言中高效地提取位的位置作为一个值

3

我正在寻找一种高效的(最好是宏)方法来提取位的位置并将其保存为C语言中的值。

data = 0x4000

请提供需要翻译的具体内容。

pos = 14

我读取的16位寄存器中只有一个位被设置。目前,我只是通过比较数据与位移值来提取位置,但肯定有更好的方法我不知道。

我花了一些时间在这里搜索类似的问题,但没有找到。


1
这是一个16位值吗?编写一个switch语句来识别哪个16位被设置。 - tadman
你要么选择使用 Switch Case,要么使用一个带有掩码 (data & (1<<n)) 的循环。 - Grégoire BOUX
2
你实际上想要实现什么?看起来你在此处试图把方形钉子放入圆孔中(XY 问题)。 - Rogue
1
你是否针对特定编译器进行开发?如果是的话,可能会有可用的内置函数可以使用。 - dbush
1
如果数据为0x807f,您预期的结果会是什么? - Devolus
显示剩余6条评论
2个回答

3
现代处理器有单独的指令来实现这些功能(计算末尾的零、查找第一个设置位、计算前导零和查找最后一个设置位)。在gcc和clang中,__builtin_ctz(n)将返回一个数字中末尾零的个数。在支持单指令ctz的处理器上,它会被编译为一条指令。请务必使用足够宽的函数(例如__builtin_ctz适用于int或更小的类型,__builtin_ctzl适用于long int或更小的类型,__builtin_ctzll适用于long long int或更小的类型)。对于16位寄存器,__builtin_ctz应该足够。
有关更多信息,请参见gcc文档维基百科

这正是我正在寻找的。谢谢。 - linsek

2

跨平台解决方案是最具可移植性和有效性的,但也比较悲观;许多现代处理器都具有像这样的位操作指令和内置函数。

例如,x86-64具有bsf指令,它将在另一个操作数中填充最高有效位的位置:

bsf eax, 0x00004000
; eax now holds the value '14'

然而,一个“纯”的C语言解决方案将会是这样的:
int MSBPos = 0;
while(data && !(data & 1)) // 'data' check avoids infinite loop if data is 0
{
    MSBPos++;
    data >>= 1;
}

请注意,这仅适用于OP的情况,其中保证整个值中只有一个设置位,并且所有其他位都为0。
不过,我不会担心它是线性算法;位运算非常快速。

为了一个好的答案点赞。__builtin_ctzl() 很容易使用,只需 3 条指令就能完成任务。2 次移动和你提到的 bsf。作为比较的一点,我也为你的 while 循环生成了汇编代码。它在纯 C 中以 +6 条指令和几个跳转完成了任务,正如你所说的那样。在我的情况下,我会利用内置函数,但对于更具可移植性的解决方案,while 循环也很有效。谢谢! - linsek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接