Java BigInteger 的按位 NOT 为负数



我正在尝试在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 输出它,这将导致以下内容:

11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000

我正在尝试使用按位 not 翻转/反转所有位。

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

根据我对位位 not 的理解,我期望所有的 1 都变成 0,所有的 0 都变成 1,但我得到以下结果:

-11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000000

这似乎也与not()函数的文档相匹配:

此方法返回负值当且仅当此 BigInteger 为非负值

是否循环遍历所有 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
}

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

有几种方法可以获取反转位。 对于它们,您将需要一个 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));

我希望一旦你也开始充实东西,掩码值将在其他地方有用。

有符号字节 64 = 01000000

反转它

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

符号"减号"是"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

将 2 的补码转换为十进制

将 1111 转换为十进制:

这个数字从 1 开始,所以它是负数,所以我们找到了 1111 的补码,即 0000。

将 1 加到 0000,我们得到 0001。

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

应用符号 = -1。

在您的情况下

当你做b.not().toString(2)时,你会得到回应:

-11111111111111111111111111111111111111111111111111111111111111110000000000000000000000000000000000000000000000000000000000000001

最后一位为1

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

例如;将所有1翻转为0,反之亦然。完成此操作后,将一个添加到解决方案中,您将获得所需的解决方案。

最终解决方案

00000000000000000000000000000000000000000000000000000000000000001111111111111111111111111111111111111111111111111111111111111111

使用

值 = 0xFFFFFFFFFFFFFFFF0000000000000000 - 这是位 1...10...0 的父项

值 = 0x0000000000000000FFFFFFFFFFFFFFFF - 这是位 0...01...1 的父项

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

如果要生成此类值,则必须执行以下操作 值 += 0x1; 值<<1; n次。在您的情况下,n 是 64。

最新更新