8

Is this Java Api's bug?

    int i = 0xD3951892;
    System.out.println(i); // -745203566
    String binString = Integer.toBinaryString(i);
    int radix = 2;
    int j = Integer.valueOf(binString, radix );
    Assertions.assertThat(j).isEqualTo(i);

I expect it to be true without any question. But it throws below exception:

java.lang.NumberFormatException: For input string: "11010011100101010001100010010010"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:495)
at java.lang.Integer.valueOf(Integer.java:556)
at com.zhugw.temp.IntegerTest.test_valueof_binary_string(IntegerTest.java:14)

So if I have a binary String , e.g. 11010011100101010001100010010010, How can I get its decimal number(-745203566) in Java? DIY? Write code to implement below equation?

enter image description here

2 Answers 2

10

Integer.valueOf(String, int radix) and Integer.parseInt(String, int radix) will only parse numbers of value -2 147 483 648 to 2 147 483 647, i.e. the values of 32-bit signed integers.

These functions cannot interpret two's complement numbers for binary (radix = 2), because the string being passed can be of any length, and so a leading 1 could be part of the number or the sign bit. I guess Java's developers decided that the most logical way to proceed is to never accept two's complement, rather than assume that a 32nd bit is a sign bit.

They read your input binary string as unsigned 3 549 763 730 (bigger than max int value). To read a negative value, you'd want to give a positive binary number with a - sign in front. For example for -5:

Integer.parseInt("1011", 2); // 11
    // Even if you extended the 1s to try and make two's complement of 5,
    // it would always read it as a positive binary value
Integer.parseInt("-101", 2); // -5, this is right

Solutions:

I suggest, first, that if you can store it as a positive number with extra sign information on your own (e.g. a - symbol), do that. For example:

String binString;
if(i < 0)
    binString = "-" + Integer.toBinaryString(-i);
else // positive i
    binString = Integer.toBinaryString(i);

If you need to use signed binary strings, in order to take a negative number in binary two's complement form (as a string) and parse it to an int, I suggest you take the two's complement manually, convert that into int, and then correct the sign. Recall that two's complement = one's complement + 1, and one's complement is just reverse each bit.

As an example implementation:

String binString = "11010011100101010001100010010010";
StringBuilder onesComplementBuilder = new StringBuilder();
for(char bit : binString.toCharArray()) {
    // if bit is '0', append a 1. if bit is '1', append a 0.
    onesComplementBuilder.append((bit == '0') ? 1 : 0);
}
String onesComplement = onesComplementBuilder.toString();
System.out.println(onesComplement); // should be the NOT of binString
int converted = Integer.valueOf(onesComplement, 2);
// two's complement = one's complement + 1. This is the positive value
// of our original binary string, so make it negative again.
int value = -(converted + 1);

You could also write your own version of Integer.parseInt for 32-bit two's complement binary numbers. This, of course, assumes you're not using Java 8 and can't just use Integer.parseUnsignedInt, which @llogiq pointed out while I was typing this.

EDIT: You could also use Long.parseLong(String, 2) first, then calculate the two's complement (and mask it by 0xFFFFFFFF), then downgrade the long down to int. Faster to write, probably faster code.

Sign up to request clarification or add additional context in comments.

4 Comments

Note that a shorter way to obtain the negative equivalent of a signed to complement number n is simply to do ~n + 1
I was trying to get around Integer.valueOf not interpreting 32-bit two's complement binary strings, though—so I essentially did ~n as a string, and did the +1 after interpreting it. However, I realise after writing that solution that it would be possible to do Long.parseLong(String, 2), do the two's complement arithmetic as numbers and then cast it back to int.
Thanks! In short, it's very non-intuitive when processing negative and radix is two. But as a general api, it can only be this.
In addtional, someone told me BIgInteger could help in this situation. e.g. BigInteger bigInteger = new BigInteger(Integer.toBinaryString(Integer.MIN_VALUE), 2); Assertions.assertThat(bigInteger.intValue()).isEqualTo(Integer.MIN_VALUE);
6

The API docs for Integer.toBinaryString(..) explicitly state:

The value of the argument can be recovered from the returned string s by calling Integer.parseUnsignedInt(s, 8).

(as of Java 8u25) I think this is a documentation error, and it should read Integer.parseUnsignedInt(s, 2). Note the Unsigned. This is because the toBinaryString output will include the sign bit.

Edit: Note that even though this looks like it would produce an unsigned value, it isn't. This is because Java does not really have a notion of unsigned values, only a few static methods to work with ints as if they were unsigned.

2 Comments

See my edit: You already have -745203566. There are no unsigned ints in Java.
Have you at least tried to use parseUnsignedInt instead of dismissing my answer? You should not need Integer.valueOf at all.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.