1

I have four byte array and I like to convert to float in Objective C. I tried three approaches and not sure what is the right approach.

What I tried are

        unsigned char *bytearray = (unsigned char *)calloc(4, sizeof(unsigned char));
        bytearray[0] = GolfResult[i]; bytearray[1] = GolfResult[i+1]; bytearray[2] = GolfResult[i+2]; bytearray[3] = GolfResult[i+3];

    Method 1

        float result = [[NSNumber numberWithUnsignedChar:*bytearray] floatValue];

    Method 2

        float result = (float)((bytearray[3] << 24) | (bytearray[2] << 16) | (bytearray[1] << 8) | bytearray[0]);

    Method 3
        float result = (float)((bytearray[0] << 24) | (bytearray[1] << 16) | (bytearray[2] << 8) | bytearray[3]);

What could be the correct method?

2
  • 1
    The first one. The others depend on the endianness of the system. en.wikipedia.org/wiki/Endianness Commented Jan 9, 2018 at 14:37
  • First you have to define, what the format of the 4 bytes are. Is it a 32-bit-IEEE format? What about endianess? Who created that 4 byte sequence? Commented Jan 10, 2018 at 7:51

1 Answer 1

2

Unfortunately none of your methods are correct as they all interpret the bytes as an integer in some way and then attempt to convert that integer to a float. This will never give the correct answer as bytes for an integer value, say 42 (0x2A bit pattern), are very different than the bytes for the equivalent floating-point value, say 42.0 (0x42280000 bit pattern which loaded as an int and converted to float produces 1109917696.0).

What is required is to interpret the bytes directly as being in floating-point format.

The next issue you must deal with is the order of the bytes as they are stored in your golfResult (1) array. Depending on the source these bytes the first (golfResult[i]) could be the least (little endian) or most (big endian) significant byte. If you assemble the 4 bytes in the wrong order then your resulting float value will be incorrect (2).

You can, and the code we present does, use an integer typed variable as a store for n bytes without considering them as an integer per se. That is if you have a pointer of type int32_t *, load a value from it and store it in an int32_t variable then you've just copied 4 bytes from one location to another preserving their order.

Apple provide the Byte Order Utilities functions to rearrange the byte-order and one of CFSwapInt32BigToHost() or CFSwapInt32LittleToHost() should be used to order your 4 bytes into whatever order your host platform is using (note that you don't need to know the host order, little or big endian, these functions know that; you just need to know the order in golfResult).

Finally it is important to know that calloc(), malloc() and friends return a pointer "aligned such that it can be used for any data type" (see man 3 malloc in Terminal). This means you can cast this pointer to be a pointer to any type and load/store without memory alignment issues.

Now that probably all sounds complicated, but in code it is short and easy. Start as you did to produce bytearray, preserving the order of the bytes:

unsigned char *bytearray = (unsigned char *)calloc(4, sizeof(unsigned char));
bytearray[0] = GolfResult[i]; bytearray[1] = GolfResult[i+1]; bytearray[2] = GolfResult[i+2]; bytearray[3] = GolfResult[i+3];

Now do the load, byte swap, and float interpretation of the bytes:

// - cast bytearray to be a pointer to
//   int32_t (used here ONLY as a 4 byte container and NOT as
//   an integer *per se*) then copy the 4 bytes to unorderedBytes
//   so the four bytes are in the same order as in bytearray.
int32_t unorderedBytes = *(int32_t *)bytearray;

// - pass those four bytes to a function to reorder them to host
//   endian order, here we pick CFSwapInt32LittleToHost, you might
//   require CFSwapInt32BigToHost
int32_t orderedBytes = CFSwapInt32LittleToHost(unorderedBytes);

// now cast the *address* of the host-ordered four bytes to
// be a float pointer and load them, interpreting them correctly
float output = *(float *)&orderedBytes;

Hope that help more than it confuses!


Notes:

(1) Did you notice that your variable GolfResult is coloured blue? This is because you've not followed the naming convention of starting variables with a lowercase letter.

(2) We only deal here with byte order, we ignore the binary format. Most systems use IEEE floating point formats, if wherever the bytes in golfResult came from did not use IEEE you have a bigger problem as you need to interpret whatever format they used. Assuming IEEE is probably safe.

Sign up to request clarification or add additional context in comments.

Comments

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.