I am attempting to send a structure containing floating point data over a network, which comprises two different hardware architectures, written in C.
The client is running on x86_64 architecture, and the server is running on PPC (32bit) architecture.
It seems the only options I have for converting data to and from network friendly formats is:
htons
/ntohs
(2 byte) and htonl
/ntohl
(4 byte)
There appears to be no version which deals with floating point numbers (which makes sense, as differing architectures/endianness has different representations of floating point numbers).
So, I attempted splitting the floating point number into integer and exponent format in the following way:
void FtoME(float num, int32_t* mantissa, int32_t* exponent)
{
int32_t sig = (int32_t)num;
int32_t exp = 0;
double frac = num-sig;
double temp = num;
while (frac > 0 && exp > -20)
{
temp = temp * 10; //left shift the whole number
sig = (int32_t)temp; //get the integer part
frac = temp - sig; //get the fractional part
exp--;
}
printf("Scientific note: %dx10^%d\n",sig, exp);
*mantissa = sig;
*exponent = exp;
}
Now, whilst this does work in theory, in practice, I run into overflows a LOT, so clearly, this is not the correct way to handle this.
Are there other approaches I might try to be able to avoid overflows, and convert the float to a network friendly format (and importantly, back again), whilst not losing any data?
The IEEE 754 standard should be enough for most architectures - and for only those that are not compliant, you just need to worry about the conversion to IEEE 754 and back again. For the byteorder stuff, given a 32-bit float, you can use uint32_t tmp; memcpy(&tmp, &f, sizeof(f));
with htonl
and ntohl
.
There appears to be no version which deals with floating point numbers (which makes sense, as differing architectures/endianness has different representations of floating point numbers).
No it does not "make sense": every data type larger than one byte is going to be affected by the endianness of the system. This includes short
and long
integers.
The htonl
and ntohl
functions are exactly what you are looking for and can also be used to send floating point numbers, which are 32 bits (NOT double
s though, which are 64 bits... that's still doable, but a little more complicated).
The htonl
function converts a 32 bit value from the host endianness to network endianness (which is big endian), while the ntohl
function converts a 32 bit value from network endianness to host endianness. All you have to do is convert your float
into a uint32_t
appropriately when sending and when receiving, and you'll be good to go.
Server:
float f = 10e-9;
uint32_t tmp;
memcpy(&tmp, &f, 4);
uint32_t data = htonl(tmp);
send_to_client(data);
// Or if you are sending as raw bytes:
send_to_client((char*)&data, 4);
Client:
uint32_t data;
data = receive_from_server();
// Or if you are receiving as raw bytes:
char *bytes = receive_from_server(4);
memcpy(&data, bytes, 4);
float f = (float)ntohl(data);
As an example of dealing with a struct
, assume you want to send this struct
over a network:
struct data {
char name[10];
uint32_t something1;
uint16_t something2;
float value1;
float value2;
};
Then you could do the following.
Server:
struct data x;
// Do something to initialize the fields of x.
char buffer[24]; // 10 + 4 + 2 + 4 + 4
uint32_t tmpl;
uint16_t tmps;
memcpy(buffer, x.name, 10);
tmpl = htonl(x.something1);
memcpy(buffer + 10, &tmpl, 4);
tmps = htons(x.something2);
memcpy(buffer + 14, &tmps, 2);
memcpy(&tmpl, &x.value1, 4)
tmpl = htonl(tmpl);
memcpy(buffer + 16, &tmpl, 4);
memcpy(&tmpl, &x.value, 4)
tmpl = htonl(tmpl);
memcpy(buffer + 20, &tmpl, 4);
send_to_client(buffer, 24);
Client:
char *buffer = receive_from_server(24);
struct data x;
uint32_t tmpl;
uint16_t tmps;
memcpy(x.name, buffer, 10);
memcpy(&tmpl, buffer + 10, 4);
x.something1 = ntohl(tmpl);
memcpy(&tmps, buffer + 14, 2);
x.something2 = ntohl(tmps);
memcpy(&tmpl, buffer + 16, 4);
tmpl = ntohl(tmpl);
memcpy(&x.value1, &tmpl, 4);
memcpy(&tmpl, buffer + 20, 4);
tmpl = ntohl(tmpl);
memcpy(&x.value2, &tmpl, 4);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With