AndroidDev
AndroidDev

Reputation: 5555

Difference in BigInteger values from a String and a byte array

Can someone please explain the difference between the below two initialisations of BigInteger.

Input:

BigInteger bi1 = new BigInteger("EF", 16);
byte[] ba = new byte[] {(byte)0xEF};
BigInteger bi2 = new BigInteger(ba);
Log.d("BIGINTEGER", "Big Integer1 = " + bi1.toString(16));
Log.d("BIGINTEGER", "Big Integer2 = " + bi2.toString(16));

Output:

Big Integer1 = ef
Big Integer2 = -11

How can I initialise a BigInteger with the value "EF" from a byte array?

Upvotes: 2

Views: 1296

Answers (4)

faizan
faizan

Reputation: 578

From the BigInteger docs

Constructor and Description

BigInteger(byte[] val)

Translates a byte array containing the two's-complement binary representation of a BigInteger into a BigInteger.

The Two's-complement is the real reason.

Lets see how...

(Byte)0xef in binary = 11101111 Now convert that back to Int and you get -17 (base 10) or -11 (base 16).

Now take a look at

byte[] ba = new byte[] {0, (byte)0xEF};

This has the (Byte)0xef but prepended by 0. Which means this array has 00000000 11101111, which when converted gives the correct result.

Why was the previous case different?

Check out 2's complement rules - SO Answer, Mandatory Wikipedia link

Another way of thinking about this

0xEF in Decimal = 239

Range of Byte = -127 to 128

We have Overflow.

239 - 128 = 111

Now count this 111 from back (Numeric data types have this circular behaviour, again due to 2's complement representation).

For example: 129.toByte = -127

(129 - 128 = 1, count from back the 1st value = -127)

Shortcut to counting from back if x>128 && x<256 then x.toByte = (x - 128) - 128

Here x = 239 so x.toByte = -17

Upvotes: 5

Tristan Wirth
Tristan Wirth

Reputation: 1

public BigInteger(byte[] val)

Translates a byte array containing the two's-complement binary representation of a BigInteger into a BigInteger. The input array is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element.

public BigInteger(String val, int radix)

Translates the String representation of a BigInteger in the specified radix into a BigInteger. [...]

Source: Oracle Java 7 Docs

Your Initialization from a bytearray does not behave as expected, because 0xEF casted to a bytearray returns {1, 1, 1, 0, 1, 1, 1, 1}.

Made to an integer according to the specs mentioned above is done as follows:

1*2^0 + 1*2^1 + 1*2^2 + 1*2^3 + 0*2^4 + 1*2^5 + 1*2^6 - 1*2^7 = -17 = -0x11

The two's-compliment causes the highest byte to be substracted, rather than being added. So adding a 0 to the beginningthe byte array should probably fix the problem:

byte[] ba = new byte[] {0, (byte)0xEF};

Upvotes: 0

You need to add a zero into the byte[] array:

byte[] myByteArray = new byte[] {0, (byte)0xEF};
BigInteger bi2 = new BigInteger(ba);
Log.d("BIGINTEGER", "Big Integer1 = " + bi1.toString(16));
Log.d("BIGINTEGER", "Big Integer2 = " + bi2.toString(16));

why?

well the reason is related to the language specification:

Decimal literals have a particular property that is not shared by hexadecimal, i.e Decimal literals are all positive [JLS 3.10.1].

To write a negative decimal constant, you need to use the unary negation operator (-) in combination with a decimal literal.

In this way, you can write any int or long value, whether positive or negative, in decimal form, and negative decimal constants are clearly identifiable by the presence of a minus sign.

Not so for hexadecimal nor octal literals. They can take on both positive and negative values. Hex and octal literals are negative if their high-order bit is set.

So after having said that, 0xFE is actually a negative number...

Upvotes: 0

Andy Turner
Andy Turner

Reputation: 140318

Put a leading zero into the byte[]:

byte[] ba = new byte[] {0, (byte)0xEF};

Ideone demo

Upvotes: 0

Related Questions