tags:

views:

789

answers:

6

Is it possible to convert floats from big to little endian? I have a value from a PowerPC(big endian)platform that I am send 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?

+2  A: 

simply reverse the four bytes works

float ntohf( 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;
}
gbrandt
that did it! Thanks!
Bobby
This will break on gcc with optimizations due to pointer aliasing. Do not do that, use union for such casting.I've been there, I've hit it.
Tomek
That is perfectly legal C code and no compiler should break it. I have tested it with VC6, Visual Studio 2008, Visual Studio 2010 and c++ Builder 2010. None of those compilers break this code.
gbrandt
@gbrand the code is legal but it doesn't mean it works. have a look here: http://xania.org/200712/cpp-strict-aliasing. You can also see how to union "cast" there.
Tomek
@Tomek: This does _not_ violate the strict aliasing rule. Both C and C++ explicitly permit _any_ type of object to be accessed as an array of char (and consequently, through a `char*`). The "cast through a union" hack from the link you posted results in undefined behavior (reading from a member of a union other than the last one written to results in undefined behavior).
James McNellis
@Tomek: Wouldn't using a union break some other rule as well? IIRC only one union member can be valid at any given time.
dalle
I realize that accessing the union through the member which was not the latest to be written to is bad (AFAIR it is dreaded UB) but it seems to work. This is why I call a template to cast this way evil_cast ;).@James: accessing as an array of char and through a pointer to char is NOT the same. For the aliasing I think going through char * is safe (I recall seeing it somewhere) but for me it is as bad as going through union. And honestly - if you want to fiddle with bits at such low level you are going to hit UB or IDB sooner or later. Which means you are at compiler's mercy.
Tomek
@Tomek: It doesn't matter whether it is bad to you; it matters whether its behavior is well-defined. Reinterpretation through a `char*` is well-defined (cf. C++03 §3.10/15). The union hack is not (cf. C++03 §9.5/1). If you are familiar with the language, then avoiding undefined or implementation defined behavior is not particularly difficult.
James McNellis
A: 

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.

tommieb75
+1  A: 

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. This thread may have more information for you:

http://www.idevapps.com/forum/archive/index.php/t-896.html

jscharf
A: 

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;
}
Heh heh. Knew that would get downvoted. Only posted it for the giggle. When I interviewed at EA many, many years ago, they had a version that did the shifts in 1, 2, 4, 8, then 16 bit widths and asked what it did.
This will also break with optimization as it breaks strict aliasing rules...
Tomek
First off, I think it was pretty clear that this was nothing more than a little entertainment. Second, I'm not sure that the aliasing rules apply. While there is the possibility that *arg could point to memory that will be owned by n, the optimizer would not find that relevant, since it never uses n again after assigning to *arg. Aliasing rules don't come into effect until you do something like: a=5;*b=7;c=a+(*b);, where the value of c cannot be computed from a cached value of a because the assignment to *b may have affected it. Though I should have said *((int*)arg) = n; =]
Well, if performance would be important, this version would be preferred over the one doing byte shuffling. Byte shuffling will exhibit partial stall once you read the value back as dword. In this case you always stay in dword size, and it can be pipelined well, with no stalls. I am not surprised EA showed it, as game developers use functions like this for time critical code.
Suma
That said, I would probably prefer a more readable version with four shifts ored together. There are some platforms however where shift performance is worse for higher shift counts (PPC), therefore the one they have is likely to perform better.
Suma
A: 

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.

Suma