48

I am having some difficulty with these two functions: byteArrayToInt and intToByteArray.

The problem is that if I use one to get to another and that result to get to the former, the results are different, as you can see from my examples below.

I cannot find the bug in the code. Any ideas are very welcome. Thanks.

public static void main(String[] args)
{
    int a = 123;
    byte[] aBytes = intToByteArray(a);
    int a2 = byteArrayToInt(aBytes);

    System.out.println(a);         // prints '123'
    System.out.println(aBytes);    // prints '[B@459189e1'
    System.out.println(a2);        // prints '2063597568
            System.out.println(intToByteArray(a2));  // prints '[B@459189e1'
}

public static int byteArrayToInt(byte[] b) 
{
    int value = 0;
    for (int i = 0; i < 4; i++) {
        int shift = (4 - 1 - i) * 8;
        value += (b[i] & 0x000000FF) << shift;
    }
    return value;
}

public static byte[] intToByteArray(int a)
{
    byte[] ret = new byte[4];
    ret[0] = (byte) (a & 0xFF);   
    ret[1] = (byte) ((a >> 8) & 0xFF);   
    ret[2] = (byte) ((a >> 16) & 0xFF);   
    ret[3] = (byte) ((a >> 24) & 0xFF);
    return ret;
}
1
  • Try removing the loop in byteArrayToInt. Commented Mar 23, 2011 at 1:18

10 Answers 10

60

Your methods should be (something like)

public static int byteArrayToInt(byte[] b) 
{
    return   b[3] & 0xFF |
            (b[2] & 0xFF) << 8 |
            (b[1] & 0xFF) << 16 |
            (b[0] & 0xFF) << 24;
}

public static byte[] intToByteArray(int a)
{
    return new byte[] {
        (byte) ((a >> 24) & 0xFF),
        (byte) ((a >> 16) & 0xFF),   
        (byte) ((a >> 8) & 0xFF),   
        (byte) (a & 0xFF)
    };
}

These methods were tested with the following code :

Random rand = new Random(System.currentTimeMillis());
byte[] b;
int a, v;
for (int i=0; i<10000000; i++) {
    a = rand.nextInt();
    b = intToByteArray(a);
    v = byteArrayToInt(b);
    if (a != v) {
        System.out.println("ERR! " + a + " != " + Arrays.toString(b) + " != " + v);
    }
}
System.out.println("Done!");
Sign up to request clarification or add additional context in comments.

9 Comments

@owlstead, yes, your code would work best to convert a byte[] into int[], however for isolated conversion of byte to int, using direct bit manipulation (especially when you know and expect the defined data to be properly formed) is a way lot faster than creating an instance of a class to do the exact same thing for each call. Even if you cache an instance of ByteBuffer, you'd still have to copy the passing bytes to the buffer's array, thus gives you some overhead in any case.
@owlstead, perhaps, but if you can optimize a method from the start, and it is a really simple one, why would you settle for less? You don't need a reusable pattern here.. and, besides, the accumulation of "small price to pay" can escalate quite fast in a big project. Think about it.
yes, this answer is a tidy old. I don't know why I used a for block for the first method...
I'm a bit confused. In the intToByteArray method, why do you have to AND 0xFF to each byte before converting it back to a byte? 0xFF would include all 8 bits. And a byte is only 8 bits. So the code ` & 0xFF` seems pointless.
@WaffleStealer654: That's needed because Java bytes are signed, so if the top bit is on it's a negative number with one bits in all the high-order bits when converted to an int. (The fact that Java bytes are signed is, well, to be polite, non-optimal in my opinion, but that's the way they are.)
|
51

That's a lot of work for:

public static int byteArrayToLeInt(byte[] b) {
    final ByteBuffer bb = ByteBuffer.wrap(b);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    return bb.getInt();
}

public static byte[] leIntToByteArray(int i) {
    final ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    bb.putInt(i);
    return bb.array();
}

This method uses the Java ByteBuffer and ByteOrder functionality in the java.nio package. This code should be preferred where readability is required. It should also be very easy to remember.

I've shown Little Endian byte order here. To create a Big Endian version you can simply leave out the call to order(ByteOrder).


In code where performance is higher priority than readability (about 10x as fast):

public static int byteArrayToLeInt(byte[] encodedValue) {
    int value = (encodedValue[3] << (Byte.SIZE * 3));
    value |= (encodedValue[2] & 0xFF) << (Byte.SIZE * 2);
    value |= (encodedValue[1] & 0xFF) << (Byte.SIZE * 1);
    value |= (encodedValue[0] & 0xFF);
    return value;
}

public static byte[] leIntToByteArray(int value) {
    byte[] encodedValue = new byte[Integer.SIZE / Byte.SIZE];
    encodedValue[3] = (byte) (value >> Byte.SIZE * 3);
    encodedValue[2] = (byte) (value >> Byte.SIZE * 2);   
    encodedValue[1] = (byte) (value >> Byte.SIZE);   
    encodedValue[0] = (byte) value;
    return encodedValue;
}

Just reverse the byte array index to count from zero to three to create a Big Endian version of this code.


Notes:

  • In Java 8 you can also make use of the Integer.BYTES constant, which is more succinct than Integer.SIZE / Byte.SIZE.

7 Comments

One liner: byte[] result = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putInt(i).array();
OK, so there was an interesting discussion with Yanick about performance, so I've added a performance enhanced version, using Yanick's answer (upvoted) as base. The most important change is the removal of the for loop of course, as branching is slow on current CPU's. It's therefore at least twice as fast.
Isn't there an inconsistency between your short version and your performance version? The sort version is for little-endian and the performance version is for big-endian?
@RenniePet Ah, yeah, should have made that more clear. I'll add a LE version of the performance version later.
From the performance version of byteArrayToLeInt, why didn't you AND the encodedValue[3] by 0xff?
|
44

You're swapping endianness between your two methods. You have intToByteArray(int a) assigning the low-order bits into ret[0], but then byteArrayToInt(byte[] b) assigns b[0] to the high-order bits of the result. You need to invert one or the other, like:

public static byte[] intToByteArray(int a)
{
    byte[] ret = new byte[4];
    ret[3] = (byte) (a & 0xFF);   
    ret[2] = (byte) ((a >> 8) & 0xFF);   
    ret[1] = (byte) ((a >> 16) & 0xFF);   
    ret[0] = (byte) ((a >> 24) & 0xFF);
    return ret;
}

1 Comment

Take a look at the ByteBuffer class, I've created an example around it.
24

You can also use BigInteger for variable length bytes. You can convert it to Long, Integer or Short, whichever suits your needs.

new BigInteger(bytes).intValue();

or to denote polarity:

new BigInteger(1, bytes).intValue();

To get bytes back just:

new BigInteger(bytes).toByteArray()

4 Comments

@Duffmaster33 Simple, but slow... and too much garbage collection pressure. But +1 anyway :)
@St.Antario Thanks for the comment, would you mind elaborating? I'm curious
@Duffmaster33 This can be seen if we run simple JMH-benchmark. I ran it with -prof gc and saw that with default parameters on a single fork 5 warmup and 5 iterations I got 16 Minor GC with allocation rate 500 MB/sec. On my machine it's about 10 times slower.
@Duffmaster33 I just decompiled the version with bitwise operations and noticed that all these movsbl 0x13(%r11),%r10d movsbl 0x11(%r11),%eax movsbl 0x12(%r11),%r8d movzbl 0x10(%r11),%r9d And these all or-ed.
2

I like owlstead's original answer, and if you don't like the idea of creating a ByteBuffer on every method call then you can reuse the ByteBuffer by calling it's .clear() and .flip() methods:

ByteBuffer _intShifter = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE)
                                   .order(ByteOrder.LITTLE_ENDIAN);

public byte[] intToByte(int value) {
    _intShifter.clear();
    _intShifter.putInt(value);      
    return _intShifter.array();
}

public int byteToInt(byte[] data)
{
    _intShifter.clear();
    _intShifter.put(data, 0, Integer.SIZE / Byte.SIZE);
    _intShifter.flip();
    return _intShifter.getInt();
}

1 Comment

Yup, just don't re-use the buffer in multi-threaded code, that would get you in trouble.
2

I found a simple way in com.google.common.primitives which is in the [Maven:com.google.guava:guava:12.0.1]

long newLong = Longs.fromByteArray(oldLongByteArray);
int newInt = Ints.fromByteArray(oldIntByteArray);

Have a nice try :)

Comments

0

I took a long look at many questions like this, and found this post... I didn't like the fact that the conversion code is duplicated for each type, so I've made a generic method to perform the task:

public static byte[] toByteArray(long value, int n)
{
    byte[] ret = new byte[n];
    ret[n-1] = (byte) ((value >> (0*8) & 0xFF);   
    ret[n-2] = (byte) ((value >> (1*8) & 0xFF);   
    ...
    ret[1] = (byte) ((value >> ((n-2)*8) & 0xFF);   
    ret[0] = (byte) ((value >> ((n-1)*8) & 0xFF);   
    return ret;
}

See full post.

1 Comment

Obviously it loops... not hand made :)
0

/*sorry this is the correct */

     public byte[] IntArrayToByteArray(int[] iarray , int sizeofintarray)
     {
       final ByteBuffer bb ;
       bb = ByteBuffer.allocate( sizeofintarray * 4);
       for(int k = 0; k < sizeofintarray ; k++)
       bb.putInt(k * 4, iar[k]);
       return bb.array();
     }

1 Comment

Welcome to SO. An explanation would improve this answer.
-1

Instead of allocating space, et al, an approach using ByteBuffer from java.nio....

byte[] arr = { 0x01, 0x00, 0x00, 0x00, 0x48, 0x01};

// say we want to consider indices 1, 2, 3, 4 {0x00, 0x00, 0x00, 0x48};
ByteBuffer bf = ByteBuffer.wrap(arr, 1, 4); // big endian by default
int num = bf.getInt();    // 72

Now, to go the other way.

ByteBuffer newBuf = ByteBuffer.allocate(4);
newBuf.putInt(num);
byte[] bytes = newBuf.array();  // [0, 0, 0, 72] {0x48 = 72}

Comments

-2

here is my implementation

public static byte[] intToByteArray(int a) {
    return BigInteger.valueOf(a).toByteArray();
}

public static int byteArrayToInt(byte[] b) {
    return new BigInteger(b).intValue();
}

2 Comments

-1, this will return a different result than asked (it returns the minimum number of bytes instead of 4 bytes). Furthermore, using BigInteger is a bit heavy for this kind of purpose.
Not as bad as I expected though, only 20x slower than an optimized version :), mine was not that much faster I must admit.

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.