编写一个名为bitpat_get()
的函数来提取指定的位集。让它接受三个参数:第一个anunsigned int
,第二个是整数起始位,第三个是位计数。按照最左边的位从0
开始的惯例,从第一个参数中提取指定数量的位并返回结果。所以这个电话bitpat_get(x, 0, 3)
从中提取最左边的三个位。电话bitpat_get(x, 3, 5)
从左边的第四位开始提取五位。
我真的不知道作者提取位是什么意思,所以我几乎可以肯定我的代码是错误的,无论它返回什么都不是预期的返回值。不过,我还是会贴出来的:
#include <stdio.h>
unsigned int bitpat_get(unsigned int from, int start, int n);
int main(void)
{
unsigned int x = 0xe1f4;
printf("%x\n", bitpat_get(x, 0, 3));
printf("%x\n", bitpat_get(x, 3, 5));
}
unsigned int bitpat_get(unsigned int from, int start, int n)
{
unsigned int result = from;
int bits;
for (bits = 0; (from >> bits) != 0; ++bits)
continue;
unsigned int mask = (((1U << n) - 1) << (bits - n - start));
result = from ^ mask;
return result;
}
输出:
1f4
fef4
最佳答案
我真的不知道作者所说的提取比特是什么意思。
我们先解决这个问题。假设您有一个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|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
所以表达式应该给出从偏移量0开始的三位,或者说
bitpat_get(x, 0, 3)
。类似地,abc
将在偏移量3处给您5位,或者bitpat_get(x, 3, 5)
。这应该足以理解你需要做什么。
就你需要做什么来实现这个目标而言,这是一个两步操作。第一种方法是实际地将位右移(a),使所需的位处于最右边的位置。这取决于三条信息:
defgh
的位宽度;要提取的偏移;以及
要提取的位数。
要移动的距离是
unsigned int
。对于第一种情况,这将是bitWidth - offset - bitsNeeded
并且您可以看到将位向右移动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 - 0 - 3 = 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|a|b|c|d|e|f|g|h|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
第二步是屏蔽掉左边那些你根本不需要的部分。我们先做第二个案子,因为那有实际效果。
掩模基本上是一系列的一位在右边,可以通过从零开始,左移一位来获得你需要的每一位位置。对于需要5位的情况,序列将是二进制的
16 - 3 - 5 = 8
,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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
对于需要三位的第一种情况,掩码将是二进制的,因此不会影响原始值,因为最左边的所有位都已经为零。
注意,您不需要在循环中执行此操作,因为正如您的代码所示,您可以使用单个表达式计算它:
unsigned mask = (1U << n) - 1U;
从你发布的代码来看,我发现了一些问题。
首先,我认为您的
111
部分是为了找出2n - 1
的位宽度,这取决于您以后对该值的使用。但是,您根据传入的值计算它,这是不正确的。你应该建立在一个比特模式的基础上,最左边的比特是一个。换言之,想想如果传入的值是3(二进制
for..continue
),那么当前循环将执行什么操作-位宽度将计算为2,因为只经过两次移位,最终将为零。所以,更好的方法是:unsigned testVal = ~0U; // all one bits
for (bits = 0; testVal != 0; ++bits, testVal = testVal >> 1)
;
其次是你的面具计算。您的代码设置为就地提取位,这意味着您只需将它们周围的所有其他位设置为零。最好把它们移到右边提取(a)。
第三,您应该知道
unsigned int
是异或操作,如果您使用一个所有位的掩码,它将反转这些位,而不是按原样提取它们。你要找的接线员是11
。举例来说,将xor运算符与
^
一起使用将给出: 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;
}
唯一棘手的地方是使用静态
&
,所以只需要计算一次。这只是一个优化,以加快事情在随后的呼吁。如果您不想这样做(例如,如果您不熟悉这些概念,或者如果第一次可能同时从多个线程调用此函数,从而导致数据竞争),只需将其替换为:unsigned bitWidth = 0;
unsigned testVal = ~0U;
while (testVal != 0) {
bitWidth++;
testVal = testVal >> 1;
}
(a)这是基于经验。你可能希望他们到位,但是,在我漫长而(偶尔)辉煌的职业生涯中,我总是发现把他们安排在轮岗部门更有用。例如,如果位11-13是某种形式的整数值,则将它们移到最右边实际上会得到值
bitpat_get(21, 11, 5)
,而不是集合bitWidth
中的值。可能不是这样,所以我提供的代码涵盖了这两种情况,如果您只是注释掉替代情况。
关于c - 从无符号int提取位的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49354105/