我遇到了这个基于输入参数生成位掩码的独特问题。例如,
如果param = 2,则掩码为0x3(11b) 如果param = 5,则掩码将为0x1F(1 1111b)
这是我在C中使用for循环实现的,类似于
int nMask = 0;
for (int i = 0; i < param; i ++) {
nMask |= (1 << i);
}
我想知道是否有更好的算法~~~
答案 0 :(得分:72)
有关像这样的位掩码需要注意的一点是,它们总是小于2的幂。
表达式1 << n
是获得2的n次幂的最简单方法。
您不希望Zero提供00000001
的位掩码,您希望它提供零。所以你需要减去一个。
mask = (1 << param) - 1;
编辑:
如果你想要一个param的特殊情况&gt; 32:
int sizeInBits = sizeof(mask) * BITS_PER_BYTE; // BITS_PER_BYTE = 8;
mask = (param >= sizeInBits ? -1 : (1 << param) - 1);
此方法适用于16位,32位或64位整数,但您可能必须明确键入“1”。
答案 1 :(得分:16)
C:
#include <limits.h> /* CHAR_BIT */
#define BIT_MASK(__TYPE__, __ONE_COUNT__) \
((__TYPE__) (-((__ONE_COUNT__) != 0))) \
& (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))
C ++:
#include <climits>
template <typename R>
static constexpr R bitmask(unsigned int const onecount)
{
// return (onecount != 0)
// ? (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount))
// : 0;
return static_cast<R>(-(onecount != 0))
& (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount));
}
BIT_MASK(unsigned int, 4) /* = 0x0000000f */
BIT_MASK(uint64_t, 26) /* = 0x0000000003ffffffULL */
#include <stdio.h>
int main()
{
unsigned int param;
for (param = 0; param <= 32; ++param)
{
printf("%u => 0x%08x\n", param, BIT_MASK(unsigned int, param));
}
return 0;
}
0 => 0x00000000
1 => 0x00000001
2 => 0x00000003
3 => 0x00000007
4 => 0x0000000f
5 => 0x0000001f
6 => 0x0000003f
7 => 0x0000007f
8 => 0x000000ff
9 => 0x000001ff
10 => 0x000003ff
11 => 0x000007ff
12 => 0x00000fff
13 => 0x00001fff
14 => 0x00003fff
15 => 0x00007fff
16 => 0x0000ffff
17 => 0x0001ffff
18 => 0x0003ffff
19 => 0x0007ffff
20 => 0x000fffff
21 => 0x001fffff
22 => 0x003fffff
23 => 0x007fffff
24 => 0x00ffffff
25 => 0x01ffffff
26 => 0x03ffffff
27 => 0x07ffffff
28 => 0x0fffffff
29 => 0x1fffffff
30 => 0x3fffffff
31 => 0x7fffffff
32 => 0xffffffff
首先,正如在其他答案中已经讨论的那样,使用>>
代替<<
,以便在移位计数等于存储类型的位数时防止出现问题价值。 (感谢Julien's answer above的想法)
为了便于讨论,让我们将unsigned int
__TYPE__
“实例化”为((unsigned int) (-((__ONE_COUNT__) != 0))) \
& (((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))
,看看会发生什么(假设目前是32位):
((sizeof(unsigned int) * CHAR_BIT)
让我们关注:
sizeof(unsigned int)
第一。 4
在编译时是已知的。根据我们的假设,它等于CHAR_BIT
。 char
表示每个字节的每8
个比特数,a.k.a。它在编译时也是已知的。它在地球上的大多数机器上等于32
。由于这个表达式在编译时是已知的,编译器可能会在编译时进行乘法并将其视为常量,在这种情况下等于((unsigned int) -1)
。
让我们转到:
0xFFFFFFFF
等于-1
。将(((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))
强制转换为任何无符号类型会在该类型中生成值“all-1s”。这部分也是编译时常量。
到目前为止,表达式为:
0xffffffffUL >> (32 - param)
实际上与:
相同param
与朱利安上面的回答相同。他回答的一个问题是,如果0
等于0xffffffffUL >> 32
,则生成表达式0xffffffffUL
,表达式的结果将是0
,而不是预期的__ONE_COUNT__
1}}! (这就是为什么我将我的参数命名为__ONE_COUNT
以强调其意图)
要解决此问题,我们只需使用0
或if-else
为?:
等于#define BIT_MASK(__TYPE__, __ONE_COUNT__) \
(((__ONE_COUNT__) != 0) \
? (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))
: 0)
添加一个特殊情况,如下所示:
((unsigned int) (-((__ONE_COUNT__) != 0)))
但是无分支代码更酷,不是吗?!让我们转到下一部分:
((__ONE_COUNT__) != 0)
让我们从最里面的表达开始到最外面的表达。参数为0
时,0
会生成1
,否则会生成(-((__ONE_COUNT__) != 0))
。参数为0
时,0
会生成-1
,否则会生成((unsigned int) (-((__ONE_COUNT__) != 0)))
。对于((unsigned int) -1)
,上面已经解释了类型转换技巧((__TYPE__) (-((__ONE_COUNT__) != 0)))
。你现在注意到这个伎俩吗?表达式:
__ONE_COUNT__
如果__ONE_COUNT__
为零,则等于“all-0s”,否则为“all-1s”。它充当我们在第一步中计算的值的位掩码。因此,如果__ONE_COUNT__
非零,则掩码无效,与Julien的答案相同。如果0
是__ONE_COUNT__ : 0 Other
------------- --------------
(__ONE_COUNT__) 0 = 0x000...0 (itself)
((__ONE_COUNT__) != 0) 0 = 0x000...0 1 = 0x000...1
((__TYPE__) (-((__ONE_COUNT__) != 0))) 0 = 0x000...0 -1 = 0xFFF...F
,它会掩盖朱利安答案的所有位,产生一个恒定的零。要想象,请观看:
{{1}}
答案 2 :(得分:8)
或者,您可以使用右移来避免(1 << param) - 1
解决方案中提到的问题。
unsigned long const mask = 0xffffffffUL >> (32 - param);
当然假设param <= 32
。
答案 3 :(得分:6)
对于那些感兴趣的人,这是在对另一个答案的评论中讨论的查找表替代方案 - 区别在于它对于32的参数正确工作。很容易扩展到64位unsigned long long
版本,如果你需要它,并且不应该在速度上有显着差异(如果它在一个紧密的内部循环中被调用,那么静态表将至少保留在L2缓存中,并且如果它是不是在一个紧密的内环然后性能差异并不重要。)
unsigned long mask2(unsigned param)
{
static const unsigned long masks[] = {
0x00000000UL, 0x00000001UL, 0x00000003UL, 0x00000007UL,
0x0000000fUL, 0x0000001fUL, 0x0000003fUL, 0x0000007fUL,
0x000000ffUL, 0x000001ffUL, 0x000003ffUL, 0x000007ffUL,
0x00000fffUL, 0x00001fffUL, 0x00003fffUL, 0x00007fffUL,
0x0000ffffUL, 0x0001ffffUL, 0x0003ffffUL, 0x0007ffffUL,
0x000fffffUL, 0x001fffffUL, 0x003fffffUL, 0x007fffffUL,
0x00ffffffUL, 0x01ffffffUL, 0x03ffffffUL, 0x07ffffffUL,
0x0fffffffUL, 0x1fffffffUL, 0x3fffffffUL, 0x7fffffffUL,
0xffffffffUL };
if (param < (sizeof masks / sizeof masks[0]))
return masks[param];
else
return 0xffffffffUL; /* Or whatever else you want to do in this error case */
}
值得指出的是,如果你需要if()
声明(因为担心某人可能会用param > 32
来称呼它),那么这并不能从其他答案中获得任何替代品:
unsigned long mask(unsigned param)
{
if (param < 32)
return (1UL << param) - 1;
else
return -1;
}
唯一的区别是后一版本需要特殊情况param >= 32
,而前一版本只需要特殊情况param > 32
。
答案 4 :(得分:1)
这个(用Java):
int mask = -1;
mask = mask << param;
mask = ~mask;
这样就可以避免查找表和硬编码整数的长度。
说明:值为-1的有符号整数用二进制表示为全1。向左移动给定的次数,将多个0添加到右侧。这将导致各种“反向掩码”。然后否定移位的结果来创建你的面具。
这可以缩短为:
int mask = ~(-1<<param);
一个例子:
int param = 5;
int mask = -1; // 11111111 (shortened for example)
mask = mask << param; // 11100000
mask = ~mask; // 00011111
答案 5 :(得分:0)
如果您担心使用(1 << param) - 1
的类C语言的溢出(当参数在最大大小类型为32或64时,由于位移位超出了类型的界限,则掩码变为0),一种解决方案我只是想到了:
const uint32_t mask = ( 1ul << ( maxBits - 1ul ) ) | ( ( 1ul << ( maxBits - 1ul ) ) - 1ul );
或者另一个例子
const uint64_t mask = ( 1ull << ( maxBits - 1ull ) ) | ( ( 1ull << ( maxBits - 1ull ) ) - 1ull );
这是一个模板化版本,请记住,您应该将其与无符号类型R一起使用:
#include <limits.h> /* CHAR_BIT */
// bits cannot be 0
template <typename R>
static constexpr R bitmask1( const R bits )
{
const R one = 1;
assert( bits >= one );
assert( bits <= sizeof( R ) * CHAR_BIT );
const R bitShift = one << ( bits - one );
return bitShift | ( bitShift - one );
}
比方说,最大位是8个字节,在第一个溢出函数中,我们有1 << 8 == 256
,当转换为字节时,它变为0。在我的函数中,我们有1 << 7 == 128
,这是一个字节可以包含,因此变成1<<7 | 1<<7 - 1
。
我尚未编译该函数,因此它可能包含错别字。
为了娱乐,这里Julien Royer充实了:
// bits can be 0
template <typename R>
static constexpr R bitmask2( const R bits )
{
const R zero = 0;
const R mask = ~zero;
const R maxBits = sizeof( R ) * CHAR_BIT;
assert( bits <= maxBits );
return mask >> ( maxBits - bits );
}
答案 6 :(得分:0)
从我的头顶上。抱歉,我正在使用手机。为了清楚起见,我假定使用64位类型,但这可以很容易地概括。
(((uint64_t) (bits < 64)) << (bits & 63)) - 1u
这是典型的(1 << bits) - 1
但无分支,没有未定义的行为,& 63
在某些平台上可以优化,并且在整个值范围内都具有正确的结果。
对于大于或等于类型宽度的移位,左(左)移位操作数变为0。
屏蔽右(左)移位操作数以避免未定义的行为,该值永远不会大于63。这只是使编译器和语言律师满意,因为当左操作数已经存在时,没有平台会添加零(对于大于63的值)。一个好的编译器应该在已经是基础指令行为的平台(例如x86)上删除& 63
掩码。
我们已经看到,大于63的值从移位中得到0的结果,但是之后相减1,则所有位都由无符号整数下溢设置,这在无符号类型上并不是未定义的行为。
答案 7 :(得分:-1)
仅供参考(谷歌),我使用以下代码为整数类型获取了全1
掩码。
在C ++中,可能只是使用:
std::numeric_limits<uint_16t>::max() // 65535