Thursday, June 25, 2009

Float and Byte Array Conversion

One of my friends raised this question: How to convert sequence of bytes into float in Java?

He/she bumped into this problem when porting C application to Java which parses UDP packets. Sounds like Java is lacking the flexibility of C whereby it could simply copy the memory block content to a pointer of any types (including struct).

While in C/C++ it is legal to do this:
[sourcecode lang="c"]
#include

char cbuf[] = { 0, 0, 96, 64 }; /* representing single precision floating point 3.5f */

int main(int argc, char **argv) {
int i;
for(i = 0; i < 4; i++) {
printf("cbuf[%d] = %d\n", i, cbuf[i]);
}

float *fp = (float *) cbuf;
printf("*fp = %3.3f\n\n", *fp);
}
[/sourcecode]

Which prints the result:
[sourcecode lang="shell"]
cbuf[0]=0
cbuf[1]=0
cbuf[2]=96
cbuf[3]=64
res = 3.500

[/sourcecode]

You just can't do that in Java (plus you won't be getting mystifying errors due to memory stomping as well).
NOTE: because we are using C language, result might be different from platform to platform, due to Little Endian/Big Endian issue.

In order to achieve the conversion in Java, you have to parse the byte array (equivalent to char[] in above C example code).

I constructed a Java utility class named FloatByteArrayUtil:

[sourcecode lang="java"]
public class FloatByteArrayUtil {
private static final int MASK = 0xff;

/**
* convert byte array (of size 4) to float
* @param test
* @return
*/
public static float byteArrayToFloat(byte test[]) {
int bits = 0;
int i = 0;
for (int shifter = 3; shifter >= 0; shifter--) {
bits |= ((int) test[i] & MASK) << (shifter * 8);
i++;
}

return Float.intBitsToFloat(bits);
}

/**
* convert float to byte array (of size 4)
* @param f
* @return
*/
public static byte[] floatToByteArray(float f) {
int i = Float.floatToRawIntBits(f);
return intToByteArray(i);
}

/**
* convert int to byte array (of size 4)
* @param param
* @return
*/
public static byte[] intToByteArray(int param) {
byte[] result = new byte[4];
for (int i = 0; i < 4; i++) {
int offset = (result.length - 1 - i) * 8;
result[i] = (byte) ((param >>> offset) & MASK);
}
return result;
}

/**
* convert byte array to String.
* @param byteArray
* @return
*/
public static String byteArrayToString(byte[] byteArray) {
StringBuilder sb = new StringBuilder("[");
if(byteArray == null) {
throw new IllegalArgumentException("byteArray must not be null");
}
int arrayLen = byteArray.length;
for(int i = 0; i < arrayLen; i++) {
sb.append(byteArray[i]);
if(i == arrayLen - 1) {
sb.append("]");
} else{
sb.append(", ");
}
}
return sb.toString();
}
}

[/sourcecode]

One good thing about Java is, we don't have the Big Endian/Little Endian issue.

This is the sample code that shows how this utility works.

[sourcecode lang="java"]

public class SampleConversion {
public static void main(String args[]) {
float source = (float) Math.exp(1);
System.out.println("source=" + source);
byte[] second = FloatByteArrayUtil.floatToByteArray(source);
System.out.println("temporary byte array=" + FloatByteArrayUtil.byteArrayToString(second));
float third = FloatByteArrayUtil.byteArrayToFloat(second);
System.out.println("result=" + third);
}
}
[/sourcecode]

Which prints:
[sourcecode lang="shell"]
source=2.7182817
temporary byte array=[64, 45, -8, 84]
result=2.7182817
[/sourcecode]

Of course you also need to take consideration of the Little Endian/Big Endian issue when parsing the bytes passed from non-Java platform, or from native binaries (I think JNI supports the conversion seamlessly).

3 comments:

  1. The Float and Byte Array Conversion for Java was really interesting and helpful, Thank you!

    ReplyDelete
  2. Nice code. One question though:

    what happens if you have a float that is bigger than the biggest integer size?

    max integer value: 2^31 - 1
    max float size -> infinity of 3.4028234 × 10^38

    ReplyDelete
  3. Hi Kenneth, good question!
    We know that there is no such way integer can contains all numeric floating point, and vice versa.
    Floating point value's mantissa (significant digit) part is very limited, while integer value doesn't use exponent!
    This is a well known issue between numerical representations.

    ReplyDelete