将n个连续位设置为1的最有效方法?

问题描述 投票:0回答:3

我想要一个函数,将数字类型的最后一位设置为

n
。例如:

1

首先,我有这个实现(
bitmask (5) = 0b11111 = 31 bitmask (0) = 0

只是围绕

mask_t
typedef
):

uint64_t

一切都很好,除了当函数命中
mask_t bitmask (unsigned short n) { return ((((mask_t) 1) << n) - 1; }

bitmask (64)
的大小)时,然后我得到
mask_t
代替设置为
bitmask (64) = 0
的64位。

所以,我有两个问题:

    为什么我会有这种行为?将
  1. 1

    向左推 64 个班次应清除寄存器并保留

    1
    ,然后应用
    0
    应使用
    -1
    填充寄存器...
    
    

  2. 实现此功能的正确方法是什么?
c bit-manipulation
3个回答
6
投票

当然,您可以只采用“左移”或“右移”掩码生成,然后对“缺失”进行特殊处理

1


n

或者

uint64_t bitmask (unsigned short n) { if (n == 64) return -((uint64_t)1); return (((uint64_t) 1) << n) - 1; }

无论哪种方式都倾向于编译为分支,尽管从技术上讲它并没有

无需

uint64_t bitmask (unsigned short n) { if (n == 0) return 0; uint64_t full = ~(uint64_t)0; return full >> (64 - n); }

也可以做到(未测试)


if

这里的想法是,我们要么将 1 左移一定量(与原始代码中相同),要么在 
uint64_t bitmask (unsigned int n) { uint64_t x = (n ^ 64) >> 6; return (x << (n & 63)) - 1; }

的情况下左移 0。将 0 左移 0 将再次变为 0,减去 1 即可设置所有 64 位。


或者,如果您使用的是现代 x64 平台并且 BZHI 可用,那么速度非常快(BZHI 在实现它的所有 CPU 上都很快)但可移植性有限的选项是:

n = 64

对于
uint64_t bitmask (unsigned int n) { return _bzhi_u64(~(uint64_t)0, n); }

来说,这甚至是明确定义的,1的实际计数将是

n > 64
,因为BZHI饱和,但它只读取索引的最低字节。
    


4
投票
未定义的行为

来自

C 标准

第 6.5.7 节:

2

对每个操作数执行整数提升。这 结果的类型是提升后的左操作数的类型。 如果值 右操作数为负数或大于或等于 提升的左操作数的宽度,行为未定义。

您需要在代码中添加对此的检查:

min(n & 0xFF, 64)



1
投票

mask_t bitmask (unsigned short n) { if (n >= 64) { return ~(mask_t)0; } else { return (((mask_t) 1) << n) - 1; } }

但是,harold 的答案是如此完整且解释得很好,所以我会选择它作为答案。

© www.soinside.com 2019 - 2024. All rights reserved.