我检查了这个问题和答案,并提出了以下想法。
int i = n-1;
uint32_t y = x;
while(y && i--) {
y = y & (y << 1);
};
y
如果有n
连续的设置位,则上述操作后为非零。接下来要做的是找到在那里设置的最不重要的值。以下代码将删除除最低有效位之外的所有设置位。
z = y - (y & (y-1));
现在我们只设置了一个位,我们需要找到位的位置。我们可以在 32 种情况下使用 switch 语句。
static inline int get_set_position(const uint32_t z) {
switch(z) {
case 0x1:
return 0;
case 0x2:
return 1;
....
.... // upto (1<<31) total 32 times.
}
return -1;
}
最后要得到我们需要 reduce 的结果n-1
。所以整个过程如下。
static inline int get_set_n_position(const uint32_t x, const uint8_t n) {
if(!n) return -1;
int i = n-1;
uint32_t y = x;
while(y && i--) {
y = y & (y << 1);
};
if(!y) return -1;
uint32_t z = y - (y & (y-1));
if(!z) return -1;
int pos = get_set_position(z);
if(pos < 0) return -1;
assert(pos >= (n-1));
return pos - (n-1);
}
现在有对大端的关注。我想我只需要为大端更改 get_set_position() 即可使其工作(假设连续设置位定义基于端序进行更改)。
让我分享一个使用gcc提供的builtin_ctzl的测试代码。
OPP_INLINE int get_set_n_position(BITSTRING_TYPE x, const uint8_t n) {
if(!n || n > BIT_PER_STRING) return -1;
int i = n-1;
while(x && i--) {
x = x & (x << 1);
};
if(!x) return -1;
int pos = __builtin_ctzl(x);
return pos - (n-1);
}
该代码在 O(1) 时间内工作,因为 32 是恒定的(正如@Qnan 注意到的那样)。如果寄存器的大小不同,它再次在 O(n) 中工作。
注意:感谢评论和单元测试,我修复了错误。