【问题标题】:Bitwise NOT of Java BigInteger is NegativeJava BigInteger 的按位非为负
【发布时间】:2017-08-27 20:51:38
【问题描述】:

我试图在 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 输出,结果如下:

111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000

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

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

从我的理解来看,我希望所有的 1 都变成 0,所有的 0 都变成 1,但我得到了以下结果:

-111111111111111111111111111111111111111111111111111000000000000000000000000000000000000000000000000000000000000000000000000

这似乎也符合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
}

【问题讨论】:

  • 我的猜测是符号位发生了什么事。
  • Java 是二进制补码。你确实得到了翻转的位,然后它打印出一个有符号的二进制数。
  • 是否可以将其视为无符号数?
  • @chrixm 无关,但为什么是BigInteger 而不是BitSet
  • 请注意,对于所有值,x.not() 等于 x.negate().subtract(BigInteger.ONE)(试试看)。我想这解释了你得到的结果。

标签: java math bit-manipulation biginteger


【解决方案1】:

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

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

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

【讨论】:

  • 将此标记为答案,因为我能够使用上面提供的代码来解决我的问题。
  • 我宁愿使用.BigInteger.ONE.shiftLeft(128),而不是相当昂贵的.BigInteger.ONE.shiftLeft(128),它给出相同的结果但速度要快得多。
  • 嗨@RudyVelthuis,你的建议肯定更清楚。
【解决方案2】:

有符号字节 64 = 01000000

反转

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

符号“减号”是“非”运算符本身:

-1000000 = 0111111

输入这个,你会看到绝对值是相等的(+1)

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

【讨论】:

    【解决方案3】:

    你的答案是完全正确的,在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
    

    【讨论】:

      【解决方案4】:

      使用

      VALUE = 0xFFFFFFFFFFFFFFFF0000000000000000 - 这是位 1...10...0 的模式

      VALUE = 0x0000000000000000FFFFFFFFFFFFFFFF - 这是位 0...01...1 的模式

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

      如果你想生成这样的值,你必须这样做 值 += 0x1; 值

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 2013-10-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多