Page 1 of 1
little endian float conversion
Posted: Wed Aug 03, 2005 6:13 pm
by ohboy
Hello!
I'm wondering if there is a standard macro or function for converting little endian float to big endian, like htonl and ntohs.
If not, how do I convert myself?
Re:little endian float conversion
Posted: Wed Aug 03, 2005 11:40 pm
by erikgreenwald
I've not seen a standard macro
I typically use a union when I have to do that kind of conversion...
converting a float to a 'fake' int might look like
Code: Select all
unsigned int
fpack (float f)
{
union
{
float f;
unsigned int i;
} v;
v.f = f;
return v.i;
}
and the other direction should be obvious... then you could do something like
Code: Select all
unsigned int v = htonl(fpack(myfloat));
write(fd, &v, sizeof(unsigned int));
to write it, and assuming a function 'funpack', similar but converse to fpack, we could read the float back with
Code: Select all
unsigned int v;
float f;
read(fd, &v, sizeof(unsigned int);
f = funpack(ntohl(v));
(hopefully I didn't mess anything up too much with that... hope it helps)
-Erik
Re:little endian float conversion
Posted: Thu Aug 04, 2005 1:06 am
by Solar
The following seems to be somewhat agreed-upon by what I found on the web.
Code: Select all
#define conv_short( x ) do { char tmp; \
char * p = (char*) &x; \
tmp = p[0]; \
p[1] = p[0]; \
p[1] = tmp; } while ( 0 )
and
Code: Select all
#define conv_long( x ) do { char tmp; \
char * p = (char*) &x; \
tmp = p[0]; \
p[0] = p[3]; \
p[3] = tmp; \
tmp = p[1]; \
p[1] = p[2]; \
p[2] = tmp; } while ( 0 )
Note that floats do not require conversions, and neither do strings. You also don't need special cases for signed types. 64bit long long would be similar to the above.
It might be worthwhile checking your CPU's instruction set for an appropriate opcode, and use that as inline assembler to improve performance. (Introduces compiler dependency, of course.)
I
have seen some much more trickier bit fiddling algorithm in a discussion on Tao's "elate" virtual processor, but forgot to write it down... :-/
Re:little endian float conversion
Posted: Thu Aug 04, 2005 10:10 am
by ohboy
What do you mean by "floats do not require conversions"?
The float values I send over the net between machines with the same endian turn up perfectly fine, but when I send between little endian and big endian they get messed up. How can you explain this?
Re:little endian float conversion
Posted: Thu Aug 04, 2005 2:40 pm
by mystran
Interesting..
AFAIK, floats are normally stored with the forms defined by that famous IEEE standard.
Are you sure that you are sending and reading the same type of float, that is, either single- or double-precision on both ends?
edit: Also check that you are not relying on any particular calling convention. Floats sometimes get promoted to doubles when passing/returning them to/from functions.
Re:little endian float conversion
Posted: Thu Aug 04, 2005 11:59 pm
by Solar
[me=Solar]slaps his forehead[/me]
Only now I realized that the whole thread was about floats...
That's right, floats are usually defined by the IEC 60559 / IEEE 754 standard. I'd be hard-pressed to name a mainstream CPU that
doesn't adhere to this. They should go without conversion between low and big endian.
Re:little endian float conversion
Posted: Fri Aug 05, 2005 5:58 am
by ohboy
I've tried to send the value 123.456f over the net, here is what I get:
big endian: 066 246 233 121 (123.456001)
little endian: 152 078 154 068 (123.456055)
As you can see, they're different.
When the big endian machine (a Mac) reads the little endian value it comes up with -0.000000f, and when the little endian machine (Windows) tries to read what the Mac sends, it's 151849983732479890000000000000000000.0f.
I can't see any pattern
Re:little endian float conversion
Posted: Fri Aug 05, 2005 6:23 am
by Solar
ohboy wrote:
As you can see, they're different.
The precision of a float is no larger than 6 digits anyway, so from the perspective of a float value, they're equal. The difference in representation is probably due to register - memory - register conversions.
So your transmission was successful. Don't scratch your head about the different binary representation.
Re:little endian float conversion
Posted: Fri Aug 05, 2005 6:44 am
by ohboy
You got me wrong.
The big endian was when the float was sent between big endian machines.
The little endian was between little endian machines.
little -> big becomes -0.000000f
big -> little becomes 151849983732479890000000000000000000.000000f
Re:little endian float conversion
Posted: Fri Aug 05, 2005 7:39 am
by Solar
Ah, OK...
(Giving data in
octal? Quick, someone fetch Doc Hoggins...)
Let's see. My x86 laptop compiles a float constant of 123.456 to:
0x79 0xe9 0xf6 0x42
This is read from hexdump. A SPARC machine I happen to have access to compiles the same constant to:
0x42 0xF6 0xE9 0x79
Again, this is hexdump output. I am very surprised to see this, as it goes against knowledge ingrained so deep I can't even say where I got it from. Sorry that I gave the (obviously) wrong answer first.
On the upside, looks like you can convert a float just like a 4-byte integer.
As to why the two machines you used for testing generated so different code... I'd still suspect register conversions, or the code you used to get the output is borked. Seeing how your numbers don't match mine (reproduced on two machines), I'd say you drew the wrong numbers from your data.
Re:little endian float conversion
Posted: Fri Aug 05, 2005 8:16 am
by ohboy
Yes, it's working now.
I'm converting the float to a u32 by using a union (as erikgreenwald proposed) then I'm just swapping the bytes.