Python的按位补码运算符(~ tilde)是如何工作的?

215
为什么~2等于-3?~运算符是如何工作的?
19个回答

322

请记住,负数是以其正数补码的形式存储的。例如,这是-2在二进制补码中的表示方式:(8位)

1111 1110

您可以通过获取数字的二进制表示形式,取反它(即翻转所有位)并加一来获得此结果。因为2的二进制表示为0000 0010,通过取反我们得到1111 1101。加一后我们就得到了上面的结果。第一位是符号位,表示为负数。
现在让我们看一下如何得出~2 = -3:
这里是2:
0000 0010

只需翻转所有位,我们就得到:

1111 1101

那么,在二进制补码中,-3是什么样子的呢?先从正数3开始:0000 0011,将所有位取反得到1111 1100,再加上1变成负数(-3),即1111 1101。

因此,如果你简单地反转2的位,就可以得到-3的二进制补码表示。

补码运算符(~)只是翻转位。机器需要解释这些位的含义。


49
还有一件事需要提一下,那就是在加1之前,取反操作被称为"取反码"。 - Chris S
3
这可能对那些不了解补码的人有所帮助。在这里阅读有关补码的信息。http://en.wikipedia.org/wiki/Ones%27_complement http://en.wikipedia.org/wiki/Two%27s_complement - Sai
1
这不是按位非运算符吗? - Braden Best
3
机器如何知道它获得的是二进制补码负数而不是更大的正数?这是因为相应语言的类型系统指示该类型为有符号整型(signed int)还是无符号整型(unsigned),从而实现区分。 - GL2014
@Ellone,你的问题已经在答案中得到了解答:“第一位是符号位,表示为负数。” - mathematrucker
显示剩余6条评论

44

~操作会将数值中所有的位都取反。

为什么~2的结果是-3,与数字的二进制位表示方式有关。数字使用二进制补码表示。

因此,2的二进制值为

00000010

当使用 ~2 操作时,会翻转二进制位,因此该值现在为:

11111101

这是 -3 的二进制表示。


2
11111101 不是等于十进制的253吗,与-3相同吗? - AKS
12
这取决于它表示有符号整数还是无符号整数。 - driis
在现实世界的编程中它有什么用途?它在竞技编程中有应用吗? - Jdeep
@driis,为什么 System.out.println((byte)(~2)); 输出的仍然是 -3 而不是 253?能否解释一下原因? - zipper
字节是一种有符号类型,范围为-128..127。 - za-ek

21

正如其他人所提到的,~ 只是翻转位(将1变为0,将0变为1),由于使用了二进制补码,所以你看到了结果。

有一件事需要补充的是,为什么要使用二进制补码,这是为了对负数进行的操作与对正数进行的操作相同。想象一下-3是哪个数字,加上3可以得到零,你会发现这个数字是1101,记住二进制的加法就像小学(十进制)加法一样,当你达到2时要进位而不是10。

 1101 +
 0011 // 3
    =
10000
    =
 0000 // lose carry bit because integers have a constant number of bits.

因此,1101-3,翻转这些位,你会得到 0010,即为二。


9

这个操作是一种补码运算,而不是取反运算。

考虑到 ~0 = -1,并从此开始计算。

取反的算法是“补码运算,加一”。

你知道吗?还有一种“反码运算”,其中逆数是对称的,并且它有一个 0 和一个 -0。


8
我知道这个问题的答案早已经发布了,但是我还是想分享一下我的回答。要找到一个数的补码,首先需要找到它的二进制等效物。在这里,十进制数 2 被表示为二进制形式的 0000 0010。现在通过反转(将所有1翻转成0,将所有0翻转成1)它的二进制表示中的所有数字来得到它的补码,结果如下:
0000 0010 → 1111 1101

这是十进制数2的补码。由于二进制数的第一位,即符号位为1,这意味着它所存储的数的符号是负数(此处所指的数不是2而是2的补码)。 现在,由于数字以二进制补码形式存储(取一个数字的补码再加1),因此要将这个二进制数1111 1101显示为十进制数,首先需要找到它的二进制补码,即:
1111 1101 → 0000 0010 + 1 → 0000 0011

这是二进制补码。二进制数 0000 0011 的十进制表示为 3。由于上面提到的符号位为1,因此最终答案为-3
提示:如果您仔细阅读此过程,则会发现一的补码运算符的结果实际上是该运算符应用的数字(操作数)加1,并加上负号。您也可以尝试使用其他数字进行操作。

为什么会加两次?我看到的是 add, flip, add0010 -> 0011 -> 1100 -> 1101 - Braden Best
1
这是一种"翻转,翻转,加"的方法。首先进行1的补码翻转。由于系统以二进制补码形式存储数据,因此在需要显示数字时,将显示所存储数字的二进制补码(即进行第二次翻转和加法运算)。 - Himanshu Aggarwal
是的,它只会是2。但由于比特在存储器中存储时,最高有效位为1,这将在后面使该数字变为负数,就像上面的答案所解释的那样。 - Himanshu Aggarwal
1
根据您的描述和我所研究的一切,这不是二进制补码,而是“常规”补码或按位非。在逻辑中,“NOT 0 = 1”和“NOT 1 = 0”。在四位系统中,“NOT 0011”(3)=“1100”(12无符号,-4有符号)。据我所知,二进制补码被定义为“(NOT n)+1”,并且用于找到一个数字的负数对应项,而不管位数如何。因此,“2c(5)= -5”。现在,它完全讲得通了。只要您称呼这个操作为它本来的名字:按位非。 - Braden Best
例子:12 - Braden Best
显示剩余2条评论

6

int a=4; System.out.println(~a); 结果为:-5

在Java中,对于任何整数,'~'都代表该数字的1's补码。 例如,我要取~4,这意味着在二进制表示中为0100。 首先, 整数的长度为四个字节,即4*8(1个字节为8位)=32。 因此,在系统内存中,4被表示为 0000 0000 0000 0000 0000 0000 0000 0100 现在,~运算符将在上述二进制数上执行1's补码

即1111 1111 1111 1111 1111 1111 1111 1011->1's补码 最高有效位表示数字的符号(负或正) 如果它是1,则符号为'-' 如果它是0,则符号为'+' 根据这个,我们的结果是一个负数, 在Java中,负数以2's补码形式存储, 我们必须将获得的结果转换为2's补码(首先执行1's补码,然后只需添加1到1's补码)。 所有的1都会变成零,除了最重要的一位1(它是我们数字的符号表示,这意味着对于其余的31位 1111 1111 1111 1111 1111 1111 1111 1011(~运算符的获得结果) 1000 0000 0000 0000 0000 0000 0000 0100(1's补码)

1 (2's补码)

1000 0000 0000 0000 0000 0000 0000 0101 现在结果为-5 查看此链接以观看视频 <[Java中的位运算符] https://youtu.be/w4pJ4cGWe9Y


3

简单地说,2的补码可以通过将所有的1反转为0,所有的0反转为1,然后加上1来计算任何数字的补码。

当N=~N时,结果总是-(N+1)。因为系统以2的补码形式存储数据,这意味着它像这样存储~N。

  ~N = -(~(~N)+1) =-(N+1). 

例如:
  N = 10  = 1010
  Than ~N  = 0101
  so ~(~N) = 1010
  so ~(~N) +1 = 1011 

现在的关键是负数是怎么来的。我的观点是假设我们有一个32位寄存器,这意味着在操作中涉及2^31 -1个位,以及将早期运算(complement) 中发生变化的一位(bit) 存储为符号位,通常是1。然后我们得到结果为~10 = -11。
~(-11)=10;
如果printf("%d",~0);,则结果为-1,上述情况是正确的;
但是在32位机器上,如果printf("%u",~0),则结果为4294967295。

3

按位取反(~)是一种一元运算符。

它的工作原理如下:

首先,将给定的十进制数转换为相应的二进制值。例如,在输入2后,它会将2转换为 0000 0010(8位二进制数)。

然后,将数字中的所有1变为0,将所有0变为1;这样,该数字就变成了1111 1101。

这就是表示-3的二进制的补码形式。

为了使用补码找到无符号值,即将 1111 1101 转换为十进制 (=4294967293),我们可以在打印时简单地使用 %u。


3

很简单:

Before starting please remember that 
 1  Positive numbers are represented directly into the memory.
 2. Whereas, negative numbers are stored in the form of 2's compliment.
 3. If MSB(Most Significant bit) is 1 then the number is negative otherwise number is 
    positive.

您正在查找 ~2:

Step:1 Represent 2 in a binary format 
       We will get, 0000 0010

Step:2 Now we have to find ~2(means 1's compliment of 2)
                  1's compliment       
       0000 0010 =================> 1111 1101 

       So, ~2 === 1111 1101, Here MSB(Most significant Bit) is 1(means negative value). So, 
       In memory it will be represented as 2's compliment(To find 2's compliment first we 
       have to find 1's compliment and then add 1 to it.)

Step3:  Finding 2's compliment of ~2 i.e 1111 1101

                   1's compliment                   Adding 1 to it
        1111 1101 =====================> 0000 0010 =================> 0000 0010
                                                                      +       1
                                                                      ---------
                                                                      0000 0011 
        So, 2's compliment of 1111 1101, is 0000 0011 


Step4:  Converting back to decimal format.
                   binary format
        0000 0011 ==============> 3
        
       In step2: we have seen that the number is negative number so the final answer would  
       be -3
                                    
                                So, ~2 === -3

3

简单来说,~ 就是要找到对称值(相对于-0.5)。

~aa 应该在0和-1的中间镜子的对称位置上对称。

-5,-4,-3,-2,-1 | 0, 1, 2, 3, 4

~0 == -1
~1 == -2
~2 == -3
~3 == -4

这是因为计算机如何表示负数。
比如说,如果正数使用 1 进行计数,那么负数将使用 0
1111 1111 == -1

1111 1110 == -2; // add one more '0' to '1111 1111'

1111 1101 == -3; // add one more '0' to '1111 1110'

最后,~i == -(i+1)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接