假设一个32位无符号整数(可以推广到任何大小的答案当然更好)。
此整数可以假设为2的幂,因此仅设置一位。
我想设置整数中的所有位,除了那些低于设置位的位。所以(为简洁起见使用8位整数)00001000
将成为11111000
。
这当然可以通过找到一个设置位然后迭代更高位来设置它们来完成。假设highest_set
返回最高设置位的位置:
uint32_t f(uint32_t x)
{
int n = highest_set(x);
for (int i = 31; i != n; --i) {
x |= 1 << i;
}
return x;
}
f
的运行时确实取决于x
的值,我觉得有一种更聪明的方法可以做到这一点。
答案 0 :(得分:4)
从概念上讲,一件容易的事就是采用x-1
然后将其与0xffffffff
进行异或。在下面的评论中将其写为~(x-1)
,就像哈罗德所做的那样,它将处理不同大小的整数,而不必改变你正在与之对抗的东西。
答案 1 :(得分:2)
右移日志(值), 或者使用1的位掩码, 向左移动日志(值)。 这应该是一个通用的解决方案,任何输入都有相同的运行时间,但不保证。
答案 2 :(得分:0)
uint32_t f(uint32_t x)
{
bool bitset=false; //C++
for (int i =0; i<sizeof(int); i++) {
if(bitset) //After the first 1
{ x |= 1 << i; }
else
{
if(x&(1<<i))
bitset=true; //if 1 found then the flag is raised
}
}
return x;
}
答案 3 :(得分:0)
易于理解的解决方案
while (!(x & 0x80000000))
x |= x << 1;
此代码不需要像上面的许多解决方案一样循环32次。但是这是一个更好的解决方案:
x = -x;
为什么呢?因为你所做的基本上是快速将数字转换为2的补码
将二进制数字手动转换为二进制补码的快捷方式是从最低有效位(LSB)开始,复制所有零(从LSB开始工作)到达最重要的位)直到达到第一个1;然后复制1,翻转所有剩余的位
https://en.wikipedia.org/wiki/Two%27s_complement#Working_from_LSB_towards_MSB
您只有一位设置,因此当您复制所有零位并反转剩余的零位时,您将获得其2的补码。您可以在示例中看到它:00001000 = 8, 11111000 = -8
。另一个例子:00010000 = 16, 11110000 = -16
如果您想知道如何否定显然没有负值的无符号值,那么how the C standard defined unsigned operations
涉及无符号操作数的计算永远不会溢出,因为无法用结果无符号整数类型表示的结果将以比结果类型可以表示的最大值大1的数量为模。
只是another definition of 2's complement,因为大于可以由结果类型表示的最大值(即UINT N _MAX + 1) case)是2 N
当然,如果您想要输入更多内容,可以使用x = -(int32_t)x;
。您还有其他解决方案
x = ~x + 1; // by 2's complement definition