47

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;
}
Nayuki
  • 17,911
  • 6
  • 53
  • 80
nunos
  • 20,479
  • 50
  • 119
  • 154

10 Answers10

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!");
Yanick Rochon
  • 51,409
  • 25
  • 133
  • 214
  • @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. – Yanick Rochon Jul 30 '12 at 17:25
  • 1
    @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. – Yanick Rochon Jul 30 '12 at 17:37
  • yes, this answer is a tidy old. I don't know why I used a `for` block for the first method... – Yanick Rochon Jul 30 '12 at 19:24
  • updated methods to remove unnecessary loops and intermediary variables – Yanick Rochon Oct 17 '12 at 23:54
  • 2
    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. – Kayla Feb 27 '14 at 20:57
  • 3
    @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.) – RenniePet Feb 24 '15 at 14:39
  • I'd say - man, who made them signed hardly live a minute when bump into cross-platform programmers on a night street. The biggest mistake in the whole languge. – Tertium Jun 04 '17 at 09:30
  • @Kayla Have to agree because we need to worry about sign-extension only in case of widening conversions. – St.Antario Oct 30 '17 at 15:10
  • Cool.......................... – linjiejun Nov 10 '21 at 03:42
50

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.
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 2
    One liner: `byte[] result = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).order(ByteOrder.LITTLE_ENDIAN).putInt(i).array();` – Maarten Bodewes Jul 10 '12 at 18:57
  • 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. – Maarten Bodewes Jul 30 '12 at 18:07
  • 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 Jul 27 '13 at 10:41
  • @RenniePet Ah, yeah, should have made that more clear. I'll add a LE version of the performance version later. – Maarten Bodewes Jul 27 '13 at 10:58
  • From the performance version of `byteArrayToLeInt`, why didn't you AND the `encodedValue[3]` by `0xff`? – mr5 Aug 01 '15 at 06:14
  • @mr5 The 0xFF is to remove the topmost bits from the converted byte. If the byte has a MSB bit set to 1 then sign extension will translate the resulting int in a negative value as well. So all the higher bits of the integer are set to 1. However, if you shift these 24 (**3** times 8) bits into the bit bucket right after that then that doesn't matter much. – Maarten Bodewes Aug 01 '15 at 13:51
  • +1 for a solution, allowing to change the endianess. This helped me correctly converting a piece of C code to readable Java. – Max Hohenegger Oct 06 '16 at 10:38
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;
}
Bruno Reis
  • 37,201
  • 11
  • 119
  • 156
aroth
  • 54,026
  • 20
  • 135
  • 176
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()
Jamel Toms
  • 4,525
  • 2
  • 27
  • 26
  • 3
    Great, simple, flexible answer. Thanks! – Duffmaster33 Sep 17 '15 at 16:23
  • @Duffmaster33 Simple, but slow... and too much garbage collection pressure. But +1 anyway :) – St.Antario Oct 30 '17 at 15:06
  • @St.Antario Thanks for the comment, would you mind elaborating? I'm curious – Duffmaster33 Oct 31 '17 at 14:30
  • 1
    @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. – St.Antario Oct 31 '17 at 14:51
  • @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. – St.Antario Oct 31 '17 at 14:59
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();
}
Matt Klein
  • 7,856
  • 6
  • 45
  • 46
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 :)

kissrain
  • 95
  • 1
  • 10
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.

TacB0sS
  • 10,106
  • 12
  • 75
  • 118
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

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}
Debosmit Ray
  • 5,228
  • 2
  • 27
  • 43
-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();
}
Dapeng
  • 1,704
  • 13
  • 25
  • 1
    -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. – Maarten Bodewes Jul 10 '12 at 18:48
  • Not as bad as I expected though, only 20x slower than an optimized version :), mine was not that much faster I must admit. – Maarten Bodewes Jul 30 '12 at 18:31