21
votes

Is it possible to convert floats from big to little endian? I have a big endian value from a PowerPC platform that I am sendING via TCP to a Windows process (little endian). This value is a float, but when I memcpy the value into a Win32 float type and then call _byteswap_ulongon that value, I always get 0.0000?

What am I doing wrong?

10
take a look at that question: stackoverflow.com/questions/1786137/…Drakosha
I think they're both IEEE format, but you should double check.Mark Ransom
What happens, if you don't call _byteswap_ulong ?Maciej Hehl
<strike>Do not implement such things. Checkout boost::endian at: boost.org/doc/libs/1_64_0/libs/endian/doc/index.html</strike>morteza
@morteza's answer, that he tried but failed to strikeout, doesn't work -- float support was removed from boost::endian because it didn't work.AnotherParker

10 Answers

36
votes

simply reverse the four bytes works

float ReverseFloat( const float inFloat )
{
   float retVal;
   char *floatToConvert = ( char* ) & inFloat;
   char *returnFloat = ( char* ) & retVal;

   // swap the bytes into a temporary buffer
   returnFloat[0] = floatToConvert[3];
   returnFloat[1] = floatToConvert[2];
   returnFloat[2] = floatToConvert[1];
   returnFloat[3] = floatToConvert[0];

   return retVal;
}
11
votes

Here is a function can reverse byte order of any type.

template <typename T>
T bswap(T val) {
    T retVal;
    char *pVal = (char*) &val;
    char *pRetVal = (char*)&retVal;
    int size = sizeof(T);
    for(int i=0; i<size; i++) {
        pRetVal[size-1-i] = pVal[i];
    }

    return retVal;
}
9
votes

I found something roughly like this a long time ago. It was good for a laugh, but ingest at your own peril. I've not even compiled it:

void * endian_swap(void * arg)
{
    unsigned int n = *((int*)arg);
    n = ((n >>  8) & 0x00ff00ff) | ((n <<  8) & 0xff00ff00);
    n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
    *arg = n;   

    return arg;
}
5
votes

An elegant way to do the byte exchange is to use a union:

float big2little (float f)
{
    union
    {
        float f;
        char b[4];
    } src, dst;

    src.f = f;
    dst.b[3] = src.b[0];
    dst.b[2] = src.b[1];
    dst.b[1] = src.b[2];
    dst.b[0] = src.b[3];
    return dst.f;
}

Following jjmerelo's recommendation to write a loop, a more generic solution could be:

typedef float number_t;
#define NUMBER_SIZE sizeof(number_t)

number_t big2little (number_t n)
{
    union
    {
        number_t n;
        char b[NUMBER_SIZE];
    } src, dst;

    src.n = n;
    for (size_t i=0; i<NUMBER_SIZE; i++)
        dst.b[i] = src.b[NUMBER_SIZE-1 - i];

    return dst.n;
}
3
votes

Don't memcpy the data directly into a float type. Keep it as char data, swap the bytes and then treat it as a float.

1
votes

It might be easier to use the ntoa and related functions to convert from network to host and from host to network..the advantage it would be portable. Here is a link to an article that explains how to do this.

1
votes

From SDL_endian.h with slight changes:

std::uint32_t Swap32(std::uint32_t x)
{
    return static_cast<std::uint32_t>((x << 24) | ((x << 8) & 0x00FF0000) |
                                      ((x >> 8) & 0x0000FF00) | (x >> 24));
}

float SwapFloat(float x)
{
    union
    {
        float f;
        std::uint32_t ui32;
    } swapper;
    swapper.f = x;
    swapper.ui32 = Swap32(swapper.ui32);
    return swapper.f;
}
0
votes

This value is a float, but when I "memcpy" the value into a win32 float type and then call _byteswap_ulong on that value, I always get 0.0000?

This should work. Can you post the code you have?

However, if you care for performance (perhaps you do not, in that case you can ignore the rest), it should be possible to avoid memcpy, either by directly loading it into the target location and swapping the bytes there, or using a swap which does the swapping while copying.

0
votes

in some case, especially on modbus: network byte order for a float is:

nfloat[0] = float[1]
nfloat[1] = float[0]
nfloat[2] = float[3]
nfloat[3] = float[2]
0
votes

Boost libraries have already been mentioned by @morteza and @AnotherParker, stating that the support for float was removed. However, it was added back in a subset of the library since they wrote their comments.

Using Boost.Endian conversion functions, version 1.77.0 as I wrote this answer, you can do the following:

float input = /* some value */;
float reversed = input;
boost::endian::endian_reverse_inplace(reversed);

Check the FAQ to learn why the support was removed then partially added back (mainly, because a reversed float may not be valid anymore) and here for the support history.