SHOTbyGUN SHOTbyGUN - 20 days ago 7
Java Question

None of the 3 bytes to integer examples work

Background

I am taking 8, 16, 24 or 32 bit audio data and converting them to integers, but BigInteger cannot be recycled and using it will waste lot of memory so I created this class to fix the memory consumption. And seems like ByteBuffer will do the job well, except if the input is 3 bytes long.

I have never done any bit or byte operations, so I am completely lost here.

Issue

None of the examples that I found on stackoverflow on 3 bytes to int do not give the wanted result. Check the bytesToInt3 method.

Question

Is there something obvious that I am doing completely wrong?

Is the return new BigInteger(byte[] data).intValue(); really the only solution to this?

Code

import java.math.BigInteger;
import java.nio.ByteBuffer;

/**
*
* @author shotbygun
*
* This class was made to get rid off BigInteger
* ---- return new BigInteger(byte[] data).intValue();
*
* Because it wastes memory
*
*/
public class BytesToInt {

private static final ByteBuffer byteBuffer2 = ByteBuffer.allocate(2);
private static final ByteBuffer byteBuffer4 = ByteBuffer.allocate(4);

/**
* @param data 8, 16, 24 or 32 bit signed-integers in byte[] format
* @return int, or throws exception if data length over 4 bytes
*/
public static int bytesToInt(byte[] data) {
if(data.length == 4)
return bytes4ToInt(data);
if(data.length == 3)
return bytes3ToInt(data);
if(data.length == 2)
return bytes2ToInt(data);
else
return (int) data[0];
}

private static int bytes4ToInt(byte[] data) {

byteBuffer4.clear();
byteBuffer4.put(data);
byteBuffer4.position(0);
return byteBuffer4.getInt();
}



// HELP
private static int bytes3ToInt(byte[] data) {

// none below seem to work, even if I swap first and last bytes
// these examples are taken from stackoverflow

//return (data[2] & 0xFF) | ((data[1] & 0xFF) << 8) | ((data[0] & 0x0F) << 16);
//return ((data[2] & 0xF) << 16) | ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
//return ((data[2] << 28) >>> 12) | (data[1] << 8) | data[0];
//return (data[0] & 255) << 16 | (data[1] & 255) << 8 | (data[2] & 255);
return (data[2] & 255) << 16 | (data[1] & 255) << 8 | (data[0] & 255);

// Only thing that works, but wastes memory
//return new BigInteger(data).intValue();
}





private static int bytes2ToInt(byte[] data) {


//return new BigInteger(data).intValue();
//ByteBuffer byteBuffer = ByteBuffer.wrap(data);

byteBuffer2.clear();
byteBuffer2.put(data);
byteBuffer2.position(0);
return (int)byteBuffer2.getShort();
}


// EVERYTHING BELOW IS JUST TESTING CODE



public static void main(String[] args) {

// Test with -666 example number
int negativeInt = -666;

// BigInteger from integer to byte[]
System.out.print("BigInteger to byteArray = ");
for(byte b : BigInteger.valueOf(negativeInt).toByteArray()) {
System.out.format("0x%x ", b);
}
System.out.println();

// ByteBuffer from integer to byte[]
printIntegerAsByteArray(negativeInt);

// Above tests will return this value, but chopped for every length
byte[] negativeByteArray2 = new byte[] {(byte)0xfd, (byte)0x66};
byte[] negativeByteArray3 = new byte[] {(byte)0xff, (byte)0xfd, (byte)0x66};
byte[] negativeByteArray4 = new byte[] {(byte)0xff, (byte)0xff, (byte)0xfd, (byte)0x66};

// Run tests with data above
System.out.println("2 bytes");
testWithData(negativeByteArray2);

System.out.println("3 bytes");
testWithData(negativeByteArray3);

System.out.println("4 bytes");
testWithData(negativeByteArray4);

}

public static void printIntegerAsByteArray(int value) {
byte[] bytes = ByteBuffer.allocate(4).putInt(value).array();

System.out.print(value + " = ");

for (byte b : bytes) {
System.out.format("0x%x ", b);
}

System.out.println();
}

private static void testWithData(byte[] data) {

// Compare our converter to BigInteger
// Which we know gives wanted result
System.out.println("Converter = " + bytesToInt(data));
System.out.println("BigInteger = " + new BigInteger(data).intValue());
}


}


Output

BigInteger to byteArray = 0xfd 0x66
-666 = 0xff 0xff 0xfd 0x66
2 bytes
Converter = -666
BigInteger = -666
3 bytes
Converter = 6749695
BigInteger = -666
4 bytes
Converter = -666
BigInteger = -666

Answer

First of all, your indices are wrong. It's not 2, 1, 0 but 0, 1, 2.

Secondly the problem is that the sign isn't being extended, so even though it would work for positive values, negative values show wrong.

If you don't mask the highest (of 24bits) byte, it will sign extend properly, filling the highest (of 32bits) byte with 0x00 for positive values or 0xFF for negative values.

return (data[0] << 16) | (data[1] & 255) << 8 | (data[2] & 255);
Comments