Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 求负数的下一次幂2_C++_Math - Fatal编程技术网

C++ 求负数的下一次幂2

C++ 求负数的下一次幂2,c++,math,C++,Math,嘿,伙计们,我正在研究计算二的下一次幂,无意中发现了一个代码,看起来像这样: 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; 当我用正数运行时,它工作得很好,但它不适用于负数,我不明白这一点,因为我认为一

嘿,伙计们,我正在研究计算二的下一次幂,无意中发现了一个代码,看起来像这样:

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;

当我用正数运行时,它工作得很好,但它不适用于负数,我不明白这一点,因为我认为一个数是正数还是负数,对于它的下一个二次幂来说都不重要。如果我们有数字5,我们想找到下一个2的幂。如果我们凭直觉思考,我们知道它是8,因为8大于5,8等于2^3。如果我尝试使用负数,我总是得到0,我不理解,因为0不是两个幂的< < /p> < P>简短的答案是因为C++标准规定了在负值上由<代码> > /Cux>运算符所产生的值是定义的,而对于正值,其结果是除以2的幂

术语“定义的实现”过于简单,意思是标准允许结果在实现之间(即编译器之间)有所不同。除其他事项外,这意味着不能保证它的行为方式与正值(明确规定了行为方式)相同

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


从数学上讲,两个补码中的右移相当于除以二的幂,四舍五入为无穷大,而不是零。对于正值,向零舍入和向-无穷大舍入具有相同的效果(零和-无穷大都小于任何正整数值)。对于一个负值,它们不舍入(舍入是零,而不是零)。简短的答案是因为C++标准规定,在负值上由<代码> >代码>运算符所产生的值是定义的,而在正值上,它的结果是除以2。 术语“定义的实现”过于简单,意思是标准允许结果在实现之间(即编译器之间)有所不同。除其他事项外,这意味着不能保证它的行为方式与正值(明确规定了行为方式)相同

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


从数学上讲,两个补码中的右移相当于除以二的幂,四舍五入为无穷大,而不是零。对于正值,向零舍入和向-无穷大舍入具有相同的效果(零和-无穷大都小于任何正整数值)。对于负值,它们不会(舍入是远离零,而不是朝向零)。

Peter根据您舍入的值对代码进行了解释。下面是另一个更按位的:

此代码中的连续移位“填充”为1s,所有位位置均低于起始时设置的最高位。让我们更详细地了解一下:

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
的编号,即只有0s,然后只有1s,这意味着形式
2^n-1
的编号。这就是为什么在末尾添加
1
,以获得2的幂。要获得正确的结果,还需要在开始时删除
1
,以补偿将在结尾添加的结果

现在,对于负数,C++标准没有定义当右移时最重要的位应该是什么。但是,在这种特定情况下,它们是

1
还是
0
并不重要,只要负数的表示在其最高有效位位置使用
1
,即几乎所有负数*

因为您总是使用
x本身,所以所有最左边的位(移位不同的位置)都将使用
1
s进行加密。在算法结束时,您将返回
0b11111…1+1
,在本例中,这意味着
0
(因为您使用2s补码,结果将是1s补码中的
1
,符号幅度中的-2位数-1+1)


*这适用于从最常用到最不常用的主要负数表示:即2s补码、符号大小和1s补码。一个不正确的例子是用于IEEE浮点指数的excess-K表示法。

Peter根据四舍五入的值对代码进行了解释。下面是另一个更按位的:

此代码中的连续移位“填充”为1s,所有位位置均低于起始时设置的最高位。让我们更详细地了解一下:

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
的编号,即只有0s,然后只有1s,这意味着形式
2^n-1
的编号。这就是为什么在末尾添加
1
,以获得2的幂。要获得正确的结果,还需要在开始时删除
1
,以补偿将在结尾添加的结果

现在,对于负数,C++标准没有定义当右移时最重要的位应该是什么。但在这种特定情况下,它们是

1
还是
0
都是无关的