1

I have a client which generates a random float number in the range [0..10]. This client sends the float value over a socket to the server and the server has to display it. I've been trying to figure out how to properly send a float over a socket and I can't get anywhere. I managed to directly send the value, but what I want to do is convert the float to network representation and back, so I want to use the functions htonl() and ntohl() but I keep failing to do so. I didn't want to copy the whole server/client code here since it's quite a bit so I produced a small simulation of what happens which creates the same problem as in the server/client code.

#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>

int main(int argc, char* argv[]) {

    uint32_t tmp, data;
    srand(time(NULL));
    float rand_float = ((float)rand() / (float)RAND_MAX) * 10;
    printf("Generated random float = %f\n", rand_float);

    // client converts to network representation(big endian) and sends the data
    memcpy(&tmp, &rand_float, sizeof(tmp));
    tmp = htonl(tmp);

    // simulate sending the data over the network
    memcpy(&data, &tmp, sizeof(data));

    // server receives the data and has to convert it to host representation (little endian in my case)
    float res = (float) ntohl(data);
    printf("After send: %f\n", res);

    return 0;
}

It's quite similar to what I want in the server/client case. As you can see, the client copies the random float data into a uint32_t variable, convert it to network representation and send it. The server receives that data in a variable data of type uint32_t, converts it to host representation and that value is cast to a float. I expected this to work. But when I run this I get something like:

Generated random float = 9.266754
After send: 1091847296.000000

Or something similar. Clearly it doesn't work. The exact behavior happens in my server/client code, I get similar output. Again, in the server/client code whenever I remove that conversion to and from the network representation and run the server and client code on my machine everything works as intended, so I'm pretty sure the problem lies somewhere in this conversion but I don't see anything wrong with the code above. I've read other questions here asking about this type of conversions (and that's where I found this method of sending floats) but they didn't solve my issue.

1 Answer 1

4

float res = (float) ntohl(data);

You have to reinterpret the bits as float, not just cast the integer to float.

You could e.g. do:

float res = 0.0;
uint32_t tmp2 = ntohl(data);
memcpy(&res, &tmp2, sizeof(tmp2));

Suppose you generate the float 3.23. It has the binary representation: 01000000010011101011100001010010, or 1078900818. You do the byteswap, you send it, you swap the bytes again. Now you have the original representation, but you just do (float)1078900818, and this is simply "1078900818". Just casting doesn't work, as floating point numbers are IEEE-754 encoded.

Sign up to request clarification or add additional context in comments.

6 Comments

Yeah, that works. Thanks a lot. But what is the difference between reinterpreting the bits as float and casting to float? I thought that casting to float does exactly that, reinterpret the bits from that memory address as float.
Casting a float, e.g. 5.23 is essentially taking everything before the point (5.23->5)
Reinterpreting the bits means, that you take the bits and reinterpret them according to e.g. the IEEE754 standard
@abcxyz: Casts perform conversions, and a conversion is an operation that takes a value of one type and produces a result in another type or format that preserves the value as well as possible. For example, when we convert the integer 3 to a float, the result is float with value 3. In the opposite direction, converting 3.25 to int yields 3 because that is the best we can do in the int format. Similarly, when we convert 0x20 to decimal, the result, 32, has the same value. And converting 5 kilograms to pounds gives 11 pounds, the same value.
@abcxyz: Some conversions are confused with reinterpreting bits, but the actual conversion is performed on a pointer. If we have an int *, a pointer to int, and we convert it to float *, then we have a pointer in a new type (pointer to float), but it has the same value in the sense it points to the same memory. If the compiler supports aliasing these types, then we can use the new pointer to access the memory. That access interprets the bits as float, but that is because the type of the pointer instructs the compiler to do that. It is not directly because of the conversion.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.