Java BigInteger 的按位非是负数

Bitwise NOT of Java BigInteger is Negative

我正在尝试对 Java 中的 128 位 BigInteger 执行按位运算。我有一个 128 位数字,前 64 位设置为 1,后 64 位设置为 0(我正在玩 IPv6 掩码)。

BigInteger b = new BigInteger(2).pow(64).subtract(BigInteger.ONE).shiftLeft(64);
System.out.println(b.toString(2));

如果我使用基数 2 输出结果如下:

111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000=00600

我正在尝试 flip/reverse 所有位使用按位非。

System.out.println(b.not().toString(2));

根据我对按位非的理解,我希望所有的 1 都变成 0,所有的 0 都变成 1,但我得到的却是:

- 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111起至高20000000000000000000000000000000000000000000000000000000000000000000000 [= 16 = 16 =]

这似乎也符合 not() 函数的文档:

This method returns a negative value if and only if this BigInteger is non-negative

是否循环遍历所有 128 位,而不是对每个单独的位执行按位运算?

更新 如果我尝试解释我试图实现的目标以提供一些背景信息,这可能会有所帮助。我正在操作 IPv6 地址,并试图根据 IPv6 掩码确定给定的 IPv6 地址是否在子网内。

根据回复,我认为以下应该可行:

例如 2001:db8:0:0:8:800:200c:417b 是否在 2001:db8::/64 内?

BigInteger n = new BigInteger(1, InetAddress.getByName("2001:db8::").getAddress());
BigInteger b = BigInteger.ONE.shiftLeft(64).subtract(BigInteger.ONE).shiftLeft(64);

// First Address in Subnet
BigInteger first = n.and(b);

// Last Address in Subnet (this is where I was having a problem as it was returning a negative number)
BigInteger MASK_128 = BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE);
BigInteger last = first.add(b.xor(MASK_128));

// Convert our test IP into BigInteger
BigInteger ip = new BigInteger(1, InetAddress.getByName("2001:db8:0:0:8:800:200c:417b").getAddress());

// Check if IP is >= first and <= last
if ((first.compareTo(ip) <= 0) && (last.compareTo(ip) >= 0)) {
  // in subnet
}

有符号字节 64 = 01000000

反转它

我们得到有符号字节 -65 = 10111111

符号"minus"是"not"运算符本身:

-1000000 = 0111111

键入此内容,您会看到绝对值等于 (+1)

System.out.println(b.toString());
System.out.println(b.not().toString());

你的回答是完全正确的,在java中一切都在恭维:

将十进制转换为二进制补码

将数字转换为二进制(暂时忽略符号)例如5 是 0101,-5 是 0101

如果数字是正数,那么您就完成了。例如5 在二进制中是 0101,使用二进制补码表示法。

这是您的解决方案。

If the number is negative then

3.1 find the complement (invert 0's and 1's) e.g. -5 is 0101 so finding the complement is 1010

3.2 Add 1 to the complement 1010 + 1 = 1011. Therefore, -5 in two's complement is 1011.

那么,如果你想在二进制中执行 2 + (-3) 怎么办? 2 + (-3) 是-1。如果您使用符号幅度来添加这些数字,您将不得不做什么? 0010 + 1101 = ?

考虑一下使用二进制补码是多么容易。

2 = 0010

-3 = 1101

+


-1 = 1111

将二进制补码转换为十进制

将 1111 转换为十进制:

1开头的数是负数,所以求1111的补数,即0000

0000加1,得到0001。

将 0001 转换为十进制,即 1。

应用符号 = -1。

你的情况

当您执行 b.not().toString(2) 时,您将得到响应:

-11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000001

在最后一位 1

现在做两人的赞美,你会得到正确的答案。

例如;将所有 1 的 翻转 0 的 ,反之亦然。这样做之后,将一个添加到解决方案中,您将得到您正在寻找的解决方案。

最终解

00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111

正如其他人所指出的,是符号位给了您不想要的结果。

有几种方法可以获得反转位。对于它们两者,您都需要一个 128 位掩码值:

private static final BigInteger MASK_128 =
    BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE);

那么你可以屏蔽符号位:

BigInteger b = BigInteger.valueOf(2).pow(64).subtract(BigInteger.ONE).shiftLeft(64);
System.out.println(MASK_128.andNot(b).toString(2));

或者直接用异或取反:

System.out.println(b.xor(MASK_128).toString(2));

我希望掩码值在您开始充实内容后在其他地方也会有用。

使用

VALUE = 0xFFFFFFFFFFFFFFFF0000000000000000 - 这是位 1...10...0

的模式

VALUE = 0x0000000000000000FFFFFFFFFFFFFFFF - 这是位 0...01...1

的模式

只需手动输入 16 x "F" 和“0”,并记得在每个模式前添加“0x”。 将此值定义为最终值。

如果你想生成这样的值你必须做 值 += 0x1; 值 << 1; 对于 n 次。在你的例子中 n 是 64.