I would like to convert a signed int into a signed byte[] array, and later convert it back into a signed int.
However, ByteBuffers (The usual int->buffer->byte[] array) are too slow for this case.
Can this be done using basic operations?
I've seem many attempts, but I haven't seen one that works in all cases. (Usually, they fail for negative numbers.)
I am working in Java, so it is not possible to use unsigned values, even in intermediate steps.
private void writeInt(int val, byte[] data, int offset) {
data[offset ] = (byte)(val >>> 24);
data[offset + 1] = (byte)(val >>> 16);
data[offset + 2] = (byte)(val >>> 8);
data[offset + 3] = (byte)val;
}
private int readInt(byte[] data, int offset) {
return (data[offset] << 24)
| ((data[offset + 1] & 0xFF) << 16)
| ((data[offset + 2] & 0xFF) << 8)
| (data[offset + 3] & 0xFF);
}
Related
I am attempting to store a single byte value into a position within an int, however, I am having trouble figuring out how this would be done.
I have shifted the byte that i want to store in the int to the correct value, but I am at a loss as to how i then combine this with the value.
public static int ibyteToInt(int b, int pos, int val)
{
return ((b & 0xff) << (8 * pos)) ??;
}
The simplest solution would be to unpack the 3 other bytes from the int and then recombine the 4 bytes into an int but of course this would not be very performant.
public static int bytesToInt(byte a, byte b, byte c, byte d)
{
return ((a & 0xff) << 0) | ((b & 0xff) << 8) | ((c & 0xff) << 16) | ((d & 0xff) << 24);
}
An other simple solution is clearing out the target byte, then OR-ing in the new value:
int replaceByte(int value, byte b, int pos)
{
return (value & ~(0xFF << (pos * 8))) | ((b & 0xFF) << (pos * 8));
}
This is very similar to how to do the same thing in C#, but Java requires & 0xFF to prevent sign-extension of the byte. If the "byte" is a value between 0 and 255 and passed in as an int, that step is not required.
I am packing my address variable which is long datatype and it is made up of datacenter, client_id, data_id and data_counter as shown below -
long address = client_data((byte) 2, (short) 100, (byte) 22, (int) 120);
private static long client_data(byte datacenter, short client_id, byte data_id, int data_counter) {
return ((long) (datacenter) << 56) | ((long) client_id << 40) | ((long) data_id << 32) | ((long) data_counter);
}
Now I want to extract datacenter, client_id, data_id and data_counter back from address variable? So I started with the below code -
To extract datacenter this is what I am using -
value = ((address >>> 56) & ((1 << 8) - 1));
To extract client_id this is what I am using -
value = ((address >>> 40) & ((1 << 16) - 1));
To extract data_id this is what I am using -
value = ((address >>> 32) & ((1 << 8) - 1));
But how do I extract data_counter? Below piece of code doesn't work for me.
value = ((address) & ((1 << 32) - 1));
(1<<32) won't do what you want because 1 is treated as a 32 bit int by default. So it tries to calculate (1<<32) as a 32 bit int. But a 32 bit int doesn't have enough bits to hold (1<<32).
Use (1L<<32). Then you will get a long, which is big enough to hold that number.
Edit:
For clarity, so you end up with:
value = (address & ((1L << 32) - 1));
Here are a couple of suggestions. Using the byte extraction method that you have proposed, it might be worth considering expressing shifts and masks in terms of Byte.SIZE, Short.SIZE and Integer.SIZE respectively. Something like this:
public static long BYTE_MASK = (1L << Byte.SIZE) - 1;
public static long SHORT_MASK = (1L << Short.SIZE) - 1;
public static long INT_MASK = (1L << Integer.SIZE) - 1;
public static long DATACENTER_SHIFT = Integer.SIZE + Byte.SIZE + Short.SIZE;
// ...and so on
Alternatively, you could use a ByteBuffer to decode the value. Something like this:
long value = 0x0200641600000078L; // example data from question
ByteBuffer bb = ByteBuffer.allocate(Long.BYTES);
bb.putLong(value);
bb.flip();
byte datacenter = bb.get();
short client_id = bb.getShort();
byte data_id = bb.get();
int data_counter = bb.getInt();
This might be worth considering if your incoming data is an array of bytes, perhaps from reading an InputStream, as you would be able to avoid assembling into a long and then disassembling into component pieces. In this case, you could use ByteBuffer.wrap(incomingByteArray) instead of allocate/putLong/flip.
I convert a short number to a 3 byte array using the following code:
static byte[] convertTo3ByteArray(short s) {
byte[] ret = new byte[3];
ret[0] = (byte) (s & 0xff);
ret[1] = (byte) ((s >> 8) & 0xff);
ret[2] = (byte) (0x00);
return ret;
}
This works very well.
I found a code on Stackoverflow to convert the array back to a number:
static int convertToInt(byte[] b) {
return ((b[0] << 0) | (b[1] << 8) | (b[2] << 16));
}
And when I convert 258 to byte array, and then use this code, it returns 258.
But for number 675, this code returns -93.
How do I have to change the convertToShort method to get 675 back?
I suppose it has something to do with bitshift and loss of data? Or signed bytes?
Try with this modified method:
static int convertToShort(byte[] b) {
return (((b[0] & 0xFF) << 0) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16));
}
In the array some bytes are negative, you need to convert them back to "positive values" with byteVal & 0xFF before doing the bit shift
A short has 16 bits of information, so that would be two bytes. When you try to store a third byte with | (b[2] << 16), it would go off the end of the short's bits, which is a problem. I.e. you can't do what you want to.
Changing to using a char datatype will fix this issue as they are the only unsigned type in Java:
https://stackoverflow.com/a/21089624/1590490
static char[] convertTo3ByteArray(short s) {
char[] ret = new char[3];
ret[0] = (char) (s & 0xff);
ret[1] = (char) ((s >> 8) & 0xff);
ret[2] = (char) (0x00);
return ret;
}
static int convertToShort(char[] b) {
return ((b[0]) | (b[1] << 8) | (b[2] << 16)); // the original << 0 shift does nothing
}
I'm trying to convert date of birth (three ints) to byte and convert it back but I'm having an issue. I have to convert it by using bit operations and send data over multicast server and receive it and change back to int. Server works fine, but bit operations are hard for me. What's the matter with the code:
Convert:
int D=12;
int M=9;
int Y=1983;
short DMY=0;
DMY = (short)(DMY | (D << 19));
DMY = (short)(DMY | (M << 15));
DMY = (short)(DMY | Y);
byte[] data = new byte[3];
data[0] = (byte)(DMY >>> 8 );
data[1] = (byte)(DMY >>> 16 );
data[2] = (byte)(DMY & 0xffff);
Convert back:
byte[] rec_data = new byte[3];
rec_data = dp.getData();
short Rec_dmy;
Rec_dmy = (short)(rec_data[0] & 0xff);
Rec_dmy = (short) (Rec_dmy << 8);
Rec_dmy = (short)(Rec_dmy | (rec_data[1] & 0xff));
Rec_dmy = (short) (Rec_dmy << 8);
Rec_dmy = (short)(Rec_dmy | (rec_data[2] & 0xffff));
byte tmp = (byte) ((Rec_dmy >>> 19) & 0x1F);
byte tmp2 = (byte) ((Rec_dmy >>> 15) & 0x1FF);
byte tmp3 = (byte) (Rec_dmy & 0x7F);
System.out.println(tmp);
System.out.println(tmp2);
System.out.println(tmp3);
Println gives following answer:
31
-1
63
It's not near original 12 9 1983
Shorts can only hold 16 bits; you are trying to pack more than that (e.g. shifting day left by 19, which will result in an all-zero value once casted to a short). You need to use an int or a long to hold all the fields.
Indeed, you've got several things going on with the bit operations that aren't right.
My suggestion would be to ditch the bit operations and just send the day, month and year as separate fields: one byte for each of the day and month, and two (a short) for the year. That takes 4 bytes (only one extra byte) but requires a lot less fiddling to get right.
Its not easy, but you have to work systematically to ensure your operations don't a) lose information b) decode the reverse of how you have encoded.
int D = 12;
int M = 9;
int Y = 1983;
int DMY = (D << 19) | (M << 15) | Y;
byte[] data = new byte[3];
data[0] = (byte) (DMY >>> 16);
data[1] = (byte) (DMY >>> 8);
data[2] = (byte) DMY;
int DMY2 = ((data[0]&0xFF) << 16) | ((data[1]&0xFF) << 8) | (data[2]&0xFF);
int D2 = DMY2 >>> 19; // no mask required
int M2 = (DMY2 >>> 15) & 0x0F; // 4 bits mask
int Y2 = DMY2 & 0x7FFF; // 15 bit mask
System.out.println(D2 + "/" + M2 + "/" + Y2);
prints
12/9/1983
First, you need at least 14 bits to represent the year with max value 9999 because of 2^14 > 9999 && 2 ^ 13 < 9999. And the least number of bits for month is 4(12 at max), day is 5(31 at max). So you can use a short int(16 bits) to represent year and byte (8 bits) for each day and month. So you get a 32-bits int.
public int encoded(short year, byte month, byte day){
int data =0;
data = year & 0xFFFF;
data =(data << 8)|(month & 0xFF)
data =(data << 8)|(day & 0xFF)
return data;
}
public void decode(int data){
int day = data & 0xFF;
int month = (data >> 8) & 0xFF;
int year = (data >> 16) & 0xFFFF;
}
Actually I need to transfer the integer value along with the bitmap via bluetooth.. Now my problem is I need to transfer the integer as single byte value..
Is tat possible to convert int as single byte value.. and retrieve it as a integer there... I tried byteValue() and the casting thing but its not usefull.. If my approach is right just help me out with this or say some other way.
(Each time when I am using casting then it's returning as 65535)
What about this?
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;
}
and
public static int byteArrayToInt(byte[] b)
{
return (b[3] & 0xFF) + ((b[2] & 0xFF) << 8) + ((b[1] & 0xFF) << 16) + ((b[0] & 0xFF) << 24);
}
If you're completely sure, that your int variable contains a byte value [-128; 127] then it should be as simple as:
int i = 100; // your int variable
byte b = (byte) i;
A single byte (8 bits) can only contain 2^8 unsigned integers, i.e [0, 255]. For signed you loose the first bit and the range becomes [-128, 127]. If your integer fits then a simple cast should work.
for 0-255 numbers.
int i = 200; // your int variable
byte b = (byte)(i & 0xFF);