我最近做了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的方法。
有没有人明白这一点,因为我不知道这里发生了什么,我打印的数字越多,我就越困惑。
谷歌似乎没有提出任何关于这一点 - 也许它不喜欢看小运营商的迹象.. :-(
来自维基百科:在二进制补码表示法中,非负数用其普通的二进制表示法表示;在这种情况下,最高有效位为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
看这个演示: -
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)
有符号整数几乎普遍存储使用 两个补充。这意味着反转位(取一个补码)并加一个。这样,您就没有两个整数零(+0和-0)的表示,并且某些签名操作变得更容易在硬件中实现。
Java使用带符号的数字 两个补码。当使用类型为“unsigned int”或“unsigned char”时,您的推理在C或其他语言中是正确的。