为负数找到2的下一个幂

时间:2016-06-03 20:10:15

标签: c++ math

嘿伙计们,我正在研究计算下一个2的幂,并偶然发现了一个代码如下:

int x;//assume that x is already initialized with a value
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return x+1;

当我使用正数运行时它工作正常,但它不适用于我不理解的负数,因为我认为在找到下一个2的幂时,数字是否为正数或负数无关紧要为了它。如果我们得到数字5并且我们想要找到下一个2的幂。如果我们直观地考虑它,我们知道它8因为8大于5而8与2 ^ 3相同。如果我尝试使用负数,我总是保持0,我不明白,因为0不是2的幂

2 个答案:

答案 0 :(得分:2)

简短的回答是因为C ++标准指出>>运算符对负值产生的值是实现定义的,而在正值上它的结果是除以2的幂。

术语"实现定义",过于简单,意味着标准允许结果在实现之间,即在编译器之间变化。除此之外,这意味着无法保证它的行为方式与正值相同(行为明确指定)。

原因是signed int的表示也是实现定义的。例如,这允许使用二进制补码表示 - 其中(尽管有时使用其他表示)在实践中非常常用。

数学上,二进制补码中的右移等效于除以2的幂,向下舍入到无穷大,而不是向零。对于正值,向零和向无穷大舍入具有相同的效果(零和无穷大都小于任何正整数值)。对于负值,它们不会(舍入远离零,而不是零)。

答案 1 :(得分:0)

彼得根据您的价值向您提供了对代码的解释。这是另一个更有意义的一个:

此代码中的连续移位是“填充”,所有位位置都低于开始时设置的最高位。让我们更详细地看一下:

x=0b0001 ???? ???? ????为16位数的二进制表示,其中?可以是0或1。

x |= x >> 1;   // x = 0b0001 ???? ???? ???? | 0b0000 1??? ???? ???? = 0b0001 1??? ???? ????
x |= x >> 2;   // x = 0b0001 1??? ???? ???? | 0b0000 011? ???? ???? = 0b0001 111? ???? ????
x |= x >> 4;   // x = 0b0001 111? ???? ???? | 0b0000 0001 111? ???? = 0b0001 1111 111? ????
x |= x >> 8;   // x = 0b0001 1111 111? ???? | 0b0000 0000 0001 1111 = 0b0001 1111 1111 1111

因此,这些转换会为您提供0b00...011...1形式的数字,该数字仅为0,然后仅为1,这意味着形式为2^n-1的数字。这就是为什么你在最后添加1以获得2的幂。为了得到正确的结果,你还需要在开始时删除1,以补偿你要添加的那个最后。

现在对于负数,C ++标准没有定义右移时最重要的位应该是什么。但是,无论它们是1还是0都与此特定情况无关,只要您对负数的表示在其最重要的位位置使用1,即对于几乎所有这些位置都是如此*

因为你总是or x与自身,所有那些最左边的位(移位不同)将与or一起1。在算法结束时,您将返回0b11111...1 + 1,在您的情况下为0(因为您使用2s补码,结果将是{1}}在1s补码和-2 比特数 - 1 + 1符号幅度)。

*这适用于从大多数到最不受欢迎的主要负数表示:即2s补码,符号幅度和1s补码。不成对的一个例子是excess-K表示,它用于IEEE浮点指数。