问题 一元“〜”运算符 - 这到底发生了什么?


我最近做了Java课程(1周速成课程),我们介绍了一些二进制数学。

这个一元〜运算符(代号我觉得它被称为?)向我们解释了:

它将位模式反转,每“0”变为“1”,每“1”变为“0”。 例如一个字节有8位。如果您有以下字节:00000000,则反转值将更改为11111111。

以上解释清晰简洁,对我来说完全有道理。直到,也就是说,我试图实现它。

鉴于这种:

byte x = 3;
byte y = 5;
System.out.println(~x);
System.out.println(~y);

输出是:

-4  
-6

我对这是怎么回事感到很困惑。

如果二进制中的+3是11,那么这个的反转将是00,这显然不是-3。

但由于一个字节中有8位,那么+3的二进制表示不应写为00000011吗?

哪个会反转为11111100.转换回十进制值,这将是252。 但是如果你把+3写成011,那么它确实会转换为100,即+4,但那么你怎么知道它是负数呢?

如果你尝试0011,转换为1100,如果你使用第一位作为符号,那么它确实变为-4。

啊 - 所以在这一点上我以为我到了某个地方。

但后来我得到了y = 5的第二个值。

我们怎么写这个?使用相同的逻辑,+ 5转换为二进制0101,其反转为1010。

而现在,我非常困惑。这看起来表示有符号值-2或无符号值+10十进制?这两个都不是-6我打印出来的。

同样,如果我将长度增加到一个字节的8位数,+ 5就是00000101,其反转变为11111010.而且我真的找不到将其转换为-6的方法。

有没有人明白这一点,因为我不知道这里发生了什么,我打印的数字越多,我就越困惑。

谷歌似乎没有提出任何关于这一点 - 也许它不喜欢看小运营商的迹象.. :-(


9568
2017-11-23 21:29


起源

Java中的所有整数数字类型都是 签。 - Marko Topolnik
@MarkoTopolnik char是一种数字类型,并且是无符号的。 - Patricia Shanahan
在所有关于它的2个补码的非常有用的答案之后,我发现这个视频似乎很好地解释了它。感谢所有回答的人。 youtube.com/watch?v=Hof95YlLQk0&NR=1&feature=endscreen - Penelope The Duck
@PatriciaShanahan如果 char 是数字,然后请解释此行的编译器消息: char c = 1, d = 2, f = c + d; - Marko Topolnik
+是导致二进制数字促销的运算符之一,(docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2),所以c + d的类型为int。从int到char的转换是一个缩小的原始转换,请参阅(docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3),要求演员。我调用char数字类型的基础是(docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.1)其中char表示整数类型,并且整数类型是数字类型。 - Patricia Shanahan


答案:


来自维基百科:在二进制补码表示法中,非负数用其普通的二进制表示法表示;在这种情况下,最高有效位为0.二进制补码操作是否定操作,因此负数由绝对值的二进制补码表示。
为了获得二进制数的二进制补码,通过使用按位NOT运算来反转或“翻转”这些位;然后将值1添加到结果值中,忽略在取两个补码0时发生的溢出。 http://en.wikipedia.org/wiki/Two%27s_complement

因此,如果你有0101 +5,那么它的倒数是1010,即-5。

虽然您没有真正读取010作为5,但是当您在开头看到1时,您知道要获得数字,您必须再次反转其余数字以获得您想要否定的正数。如果这是有道理的。

如果您以前没有使用它,这是一个外星人的概念。它当然不是十进制数字的工作方式,但一旦你看到发生了什么就很简单。

值为8十进制写为01010,否定为10101.第一个数字(1)表示它为负数,然后将其余部分翻转以获得数值:1010。

要记住的一点是,二进制补码与普通的旧二进制系统计数不同。在正常二进制中,10101的值(在二进制补码中是-8,如上所述)当然是21.我想这就是混乱的来源 - 你如何通过观察它们来区分它们?您必须知道使用了哪种表示形式才能确定数字的实际值。还有一个补码略有不同。

这里给出了一个关于二进制数学的好教程,包括One和Two的补码。 http://www.math.grin.edu/~rebelsky/Courses/152/97F/Readings/student-binary


4
2017-11-27 22:25



原来如此。我试图像正数一样读它们,但前面有一个1。感谢那。对于链接 - 他们是辉煌的。我终于明白了。我认为。 :-) - Penelope The Duck
我想补充一点,这一切都发生在32位值。所以,当你说3时,它实际上是二进制00000000000000000000000000000011。当你反转它时变为11111111111111111111111111111100,这相当于二进制补码32位字中的-4。 - Shamal Karunarathne


看这个演示: -

3 ->  0011
~3 -> 1100  -> -4 (2's complement)

5 -> 0101
~5 -> 1010 -> -6 (2's complement)

由于有符号整数存储为2的补码,因此 2's complement 的 1100 给你 4。从那以后 1100 是一个负数。所以,结果是 -4。情况也是如此 1010

1100 
0011  - 1's complement
0100  - 2's complement  - value = 4 (take negative)

8
2017-11-23 21:32



另外,请注意,按位补码运算符执行一元数字提升。 - Nathan Ryan
2的补充。嗯。我们的教练没有提到那一点。我去了谷歌。谢谢。 :-) - Penelope The Duck


有符号整数几乎普遍存储使用 两个补充。这意味着反转位(取一个补码)并加一个。这样,您就没有两个整数零(+0和-0)的表示,并且某些签名操作变得更容易在硬件中实现。


2
2017-11-23 21:33





Java使用带符号的数字 两个补码。当使用类型为“unsigned int”或“unsigned char”时,您的推理在C或其他语言中是正确的。


2
2017-11-23 21:34