little endian float conversion

Programming, for all ages and all languages.
Post Reply
ohboy

little endian float conversion

Post 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?
erikgreenwald

Re:little endian float conversion

Post 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
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:little endian float conversion

Post 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... :-/
Every good solution is obvious once you've found it.
ohboy

Re:little endian float conversion

Post 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?
mystran

Re:little endian float conversion

Post 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.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:little endian float conversion

Post by Solar »

[me=Solar]slaps his forehead[/me]

Only now I realized that the whole thread was about floats... :D

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.
Every good solution is obvious once you've found it.
ohboy

Re:little endian float conversion

Post 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 :(
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:little endian float conversion

Post 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. ;)
Every good solution is obvious once you've found it.
ohboy

Re:little endian float conversion

Post 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
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re:little endian float conversion

Post 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.
Every good solution is obvious once you've found it.
ohboy

Re:little endian float conversion

Post 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.
Post Reply