按位否定会产生意外结果

时间:2013-03-22 16:38:21

标签: java bit-manipulation

我正在尝试在java中编写一个按位计算器,你可以输入一个表达式,例如~101,但是当我运行这段代码时它会返回10

import java.util.Scanner;

public class Test
{
    public static void main(String[] args)
    {
        Integer a = Integer.valueOf("101", 2);
        System.out.println(Integer.toString(~a,2));
    }
}

它输出-110为什么?

5 个答案:

答案 0 :(得分:11)

您假设101是三位长。 Java不支持可变长度位操作,它在整个int位上运行,因此~将是32位长“101”的not

---被问到“如何解决这个问题?”后编辑。 ---

这是一个非常好的问题,但答案是“你不能”和“你可以通过不同方式达到同样的目标”的混合。

您无法修复~运算符,因为它执行的操作。这有点像要求修复+只添加1的位置。只是不会发生。

您可以实现所需的操作,但需要更多“东西”才能实现。首先,您必须有某些(另一个int)来指定感兴趣的。这通常称为位掩码

 int mask = 0x00000007; // just the last 3 bits.

 int masked_inverse = (~value) & mask;

注意我们所做的实际上是反转32位,然后将这些位中的29位清零;因为,它们在面具中被设置为零,这意味着“我们不关心它们”。这也可以想象为利用&运算符,以便我们说“如果设置并且我们关心它,请设置它”。

现在你仍然有32位,但只有低3位将被反转。如果你想要一个3位数据结构,那么这是一个不同的故事。 Java(和大多数语言)只是不直接支持这些东西。因此,您可能想要在Java中添加另一个类型来支持它。 Java通过机制添加类型,但内置类型不可更改。这意味着您可以编写一个类来表示3位数据结构,但它必须在内部处理整数为32位字段。

幸运的是,有人已经这样做了。它是标准Java库的一部分,is called a BitSet

BitSet threeBits = new BitSet(3);
threeBits.set(2);  // set bit index 2
threeBits.set(0);  // set bit index 0
threeBits.flip(0,3);

然而,由于Java中的类/对象系统的限制,这种位操作对它们有不同的感觉,这是通过将类定义为唯一的方式来在Java中添加新类型。

答案 1 :(得分:2)

如果a = ...0000101 (bin) = 5 (dec)

~a = ~...0000101(bin) = ...1111010(bin) 

和Java使用“Two's complement”表单来表示负数,所以

~a = -6 (dec)

现在Integer.toBinaryString(number)Integer.toString(number, 2)之间的负数差异是

  • toBinaryString以“Two's complement”形式返回字符串,但
  • toString(number, 2)计算二进制形式,如果数字为正数,如果参数为负数,则添加“减号”标记。

因此toString(number, 2) ~a = -6

  1. 计算6的二进制值 - > 0000110
  2. 修剪前导零 - > 110
  3. 添加减号 - > -110

答案 2 :(得分:0)

toString()方法将其参数解释为有符号值。

为了演示二进制操作,最好使用Integer.toBinaryString()。它将其参数解释为无符号,因此~101输出为1111111111111111111111111111111010。

如果您想要更少的输出位,可以使用&。

屏蔽结果

答案 3 :(得分:0)

整数101实际上表示为00000000000000000000000000000101否定此项,您得到11111111111111111111111111111010 - 这是-6

答案 4 :(得分:0)

只是详细说明Edwin的答案 - 如果您正在寻找创建可变长度掩码来开发感兴趣的部分,您可能需要一些辅助函数:

/**
 * Negate a number, specifying the bits of interest.
 * 
 * Negating 52 with an interest of 6 would result in 11 (from 110100 to 001011).
 * Negating 0 with an interest of 32 would result in -1 (equivalent to ~0).
 * 
 * @param number the number to negate.
 * @param bitsOfInterest the bits we're interested in limiting ourself to (32 maximum).
 * @return the negated number.
 */
public int negate(int number, int bitsOfInterest) {
    int negated = ~number;
    int mask = ~0 >>> (32 - bitsOfInterest);
    logger.info("Mask for negation is [" + Integer.toBinaryString(mask) + "]");
    return negated & mask;
}

/**
 * Negate a number, assuming we're interesting in negation of all 31 bits (exluding the sign).
 * 
 * Negating 32 in this case would result in ({@link Integer#MAX_VALUE} - 32).
 * 
 * @param number the number to negate.
 * @return the negated number.
 */
public int negate(int number) {
    return negate(number, 31);
}