Sending float Windows <-> Linux

Started by
10 comments, last by Tribad 11 years, 9 months ago
[color=#000000][font=verdana, arial, sans-serif]

I send a float equal to 4.000 from Windows to an Amazon EC2 Linux server, but the server reads it as 8.47522e+11.[/font]
[color=#000000][font=verdana, arial, sans-serif]

The server sends back 4.000 but Windows reads it as 0.0.[/font]
[color=#000000][font=verdana, arial, sans-serif]

I tried changing the byte order using this on Windows:[/font]

[color=#000000][font=verdana, arial, sans-serif]

[background=rgb(242, 242, 242)]void ReverseFloat(float* f)
{
unsigned char* uc = (unsigned char*)f;
unsigned char temp = uc[0];
uc[0] = uc[3];
uc[3] = temp;
temp = uc[1];
uc[1] = uc[2];
uc[2] = temp;
}
[/background][/font]

[color=#000000][font=verdana, arial, sans-serif]

But the server now gets 8.47518e+11 and Windows gets 0.0.[/font]
[color=#000000][font=verdana, arial, sans-serif]

The struct is packed and it still gives me the wrong result.[/font]

[color=#000000][font=verdana, arial, sans-serif]

[background=rgb(242, 242, 242)]#if !defined( _SERVER )
#pragma pack(push, 1)
#endif
typedef struct
{
unsigned char type;
float version;
char username[16];
char password[16];
}
#if defined( _SERVER )
__attribute__ ((packed))
#endif
LoginPacket;
#if !defined( _SERVER )
#pragma pack(pop)
#endif
[/background][/font]

The client sends 37 bytes and server receives 37 bytes.

The server sends back this:

#if !defined( _SERVER )
#pragma pack(push, 1)
#endif
typedef struct
{
unsigned char type;
float version;
}
#if defined( _SERVER )
__attribute__ ((packed))
#endif
WrongVersionPacket;
#if !defined( _SERVER )
#pragma pack(pop)
#endif


The server says it sends 8 bytes for some reason and the client receives 8. I don't know why it's 8... should be 5. Is a float 7 bytes on Amazon Linux?

Advertisement
You might try checking what is the standard used by the compilers on both systems. Although I'd not use floating point for data that is sent over the network. It's a can of worms in general. It's better to send the floating point as a string or send fixed a point number, if it isn't vital to send a floating point number. If you specifically need to send floating point numbers, it might be better to use protobuf or some other library that handles the differences internally.
Your problem is probably in the struct packing of the different compilers, not in the byte representation of the floats. In fact, I think your pack push/pop is causing the problem, because GCC doesn't care about those directives.
Most EC2 servers and most Windows machines use the x86 or x86/64 hardware platform, which encodes floats the same -- as IEEE-753 4-byte little-endian values.

The easiest way to make structs largely the same across compilers is to sort your data members from largest natural alignment to smallest -- first double, then int64, then pointer, then size_t, then float, then int, then short, then char.
If you don't want to re-order the struct members, then the second option is to go through a marshalling/de-marshalling library, rather than doing straight byte copies.
enum Bool { True, False, FileNotFound };
Seconding the marshaling system.

The x86 platform is one of the few that allows mismatched alignment of integers and floats. Most other systems will crash on misaligned data. Even the x86 stopped supporting misaligned data with the newer extended data types; anything after '95 such as the 128-bit packed types.

A marshaling system will ensure that you don't violate alignment constraints which could crash your app.
Oh, wide char
There is no wide char in your struct; it has nothing to do with the problem as posted.
enum Bool { True, False, FileNotFound };
I'd treat sending data through a network the same way as storing data in files, i.e. don't trust anything about whoever else may end up accessing the data, only process raw unsigned bytes and nothing else, and use that as a basis for other types (larger integers, floating point, strings, etc.).
Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.

There is no wide char in your struct; it has nothing to do with the problem as posted.

It's using 4 bytes per char like in Java
That is incorrect. The char is still only 1 byte. There are 3 bytes of padding due to struct alignment for the following member (the float).

Put 4 chars in a struct all by themselves and your struct will only be 4 bytes in size.
I changed it to uint8_t and it works

This topic is closed to new replies.

Advertisement