我真的不知道作者所说的提取比特是什么意思。
让我们先解决这个问题。假设有一个16位无符号整数,则位位置为:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
所以表达式
bitpat_get(x, 0, 3)
应该给出从偏移量0开始的三位,或者
abc
. 同样地,
bitpat_get(x, 3, 5)
会给你偏移量3的5位,或者
defgh
。
这应该足以
懂
你需要做什么。
在你需要的方面
做
要实现这一点,需要两步操作。第一个是将位实际右移
(a)
这样你所需要的人就处于最正确的位置。这取决于三条信息:
-
的位宽度
unsigned int
;
-
要提取的偏移量;和
-
要提取的位数。
移动距离为
bitWidth - offset - bitsNeeded
. 对于你的第一个病例
16 - 0 - 3 = 13
您可以看到,将位右移13将把所需的位放在最右边的部分:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|0|0|0|0|0|a|b|c|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
第二种情况下,右移
16 - 3 - 5 = 8
为您提供:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|a|b|c|d|e|f|g|h|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
第二步是屏蔽掉左边实际上不需要的部分。我们先做第二种情况,因为这有实际效果。
掩码基本上是右侧一个位的序列,可以从零和开始获得
左边
为您需要的每个位位置移动一个位。对于需要5位的情况,序列将是二进制的
0
,则,
1
,则,
11
,则,
111
,则,
1111
和
11111
. 按位并将其与值相加将得到:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|a|b|c|d|e|f|g|h| <- value
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|0|0|0|1|1|1|1|1| <- "and" with
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|0|0|0|d|e|f|g|h| <- gives
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
对于第一种需要三位的情况,掩码将是二进制的
111
因此不会对原始值产生影响,因为所有最左边的位都是
已经
零
请注意,您不需要在循环中执行此操作,因为正如代码所示,您可以使用单个表达式进行计算
2
n
- 1
:
unsigned mask = (1U << n) - 1U;
就您发布的代码而言,我发现了一些问题。
首先,我认为
for..continue
第节旨在找出
无符号整型
,基于您以后对该值的使用。但是,您根据传入的值计算它,这是不正确的。你怎么了
应该
它的基础是位模式,其中最左边的位是一位。
换言之,如果传入的值为3(二进制),请考虑当前循环将执行的操作
11
)-位宽度将计算为2,因为仅在两次移位后,最终将得到一个零值。因此,更好的方法是:
unsigned testVal = ~0U; // all one bits
for (bits = 0; testVal != 0; ++bits, testVal = testVal >> 1)
;
其次是你的面具计算。您的代码被设置为提取到位的位,这意味着您只需将它们周围的所有其他位设置为零。最好将它们移到右侧进行提取
(a)
。
第三,你应该意识到
^
是
异或
如果与所有一位的掩码一起使用,则将
倒转
而不是按原样提取它们。你要找的接线员是
&
。
例如,使用xor运算符
bitpat_get(21, 11, 5)
将提供:
1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|0|0|0|1|0|1|0|1| <- value (21)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|0|0|0|1|1|1|1|1| <- "xor" with
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|0|0|0|0|0|0|0|0|0|0|0|1|0|1|0| <- `01010` (10): NOT the correct `10101`
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
话虽如此,我还是将函数编写为:
unsigned bitpat_get(unsigned from, unsigned start, unsigned count) {
// Only need calculate this once, first time it's called.
static unsigned bitWidth = 0;
if (bitWidth == 0) {
unsigned testVal = ~0U;
while (testVal != 0) {
bitWidth++;
testVal = testVal >> 1;
}
}
// Get the value you need to shift by.
unsigned shiftCount = bitWidth - start - count;
// Use this line if in-place bits needed.
// unsigned mask = ((1U << count) - 1U) << shiftCount;
// Or use these two lines if you need it on the right.
from = from >> shiftCount;
unsigned mask = (1U << count) - 1U;
// Mask and return the bits.
unsigned result = from & mask;
return result;
}
唯一棘手的是使用静态
bitWidth
所以它只需要计算一次。这只是为了在后续通话中加快速度而进行的优化。如果您不想这样做(例如,如果您不熟悉这些概念,或者如果有可能第一次从多个线程同时调用此函数,从而导致数据争用),只需将其替换为:
unsigned bitWidth = 0;
unsigned testVal = ~0U;
while (testVal != 0) {
bitWidth++;
testVal = testVal >> 1;
}
(a)
这是基于经验。它是
可能的
你可能想让他们就位,但在我漫长且(偶尔)杰出的职业生涯中,我总是发现让他们在移位部分更有用。例如,如果位11-13是某种类型的整数值,则实际上将其移动到最右边的位
给予
您了解价值
0..7
而不是集合中的值
{0, 4, 8, ..., 28}
。
那个
也许
事实并非如此,因此如果您只需注释掉另一种情况,那么我提供的代码将涵盖这两种情况。