views:

318

answers:

4

Hi everyone,

Could someone please help me with byte ordering regarding floating point variables? Actually the code is working correctly on Solaris, but not on Windows Xp. Here is a piece example of my code: ....

 int x_snd=1;
 float y_snd=1.13;
 struct {
      int xx_snd;
      float yy_snd;
    } data_snd;
 int x_rec;
 float y_rec;
    struct {
      int xx_rec;
      float yy_rec;
    } data_rec;  

 //marshalling 
 data_snd.xx_snd=htons(x_snd);
 data_snd.yy_snd=htonl(*(int*) &y_snd);

 //write data to socket
 send(sock1, &data_snd, ...

 //clean ...

 //read data from socket
 if recv(sock, &data_rec ...

  //unmarshalling
  x_rec=ntohs(data_rec.xx_rec);
  y_rec= *(float*) &(ntohl(data_rec.yy_rec));

...

Th code was compiled with gcc on Unix and with MSVC++6 on wndows. Any of your help would be very appreciated and I would be glad if you could direct me to any link or document that gives usefull information about endianness ...

Thanking you in advance for your help, mk

+2  A: 

This is a generally bad idea. The format of floating point numbers isn't necessarily the same on different hardware, even after accounting for byte order. I would have to recommend shipping them here and there as strings.

bmargulies
+1  A: 

Assuming both systems have the same floating point format, yy_rec is a float; ntohl takes an unsigned long; the floating point value, potentially with incorrect endianness, is being converted to an integer representation when it is passed into ntohl.

You should be getting a compiler warning due to the conversion from float to unsigned long.

James McNellis
This is crucial. The code as written ( y_rec= *(float*) ) shouldn't ever work, as it will convert yy_rec (a float) to an unsigned long and then flip its bytes.
bmargulies
A: 

thanks for your prompt answers. that is very kinf of you

just to clarify some thing ... Acoording to the material I used, it is mentioned that these funtions of network (I mean htonl ...) work with integer . But you are right as it is for unsigned long since I get the compiler warming ... Thanks!

Do you think it is not bad to woth with unsigned long? I mean intead of converting int to float and float to int, I convert float to unsigned long and than unsingned long to float ...

Thanks again for all your help, mk

make
Well, there are two issues. First, you have to be sure that both platforms use the same format for floating point numbers (generally IEEE 754, nowadays). Second, you have to know what the endianness of floating point values on each machine is; note well that on some machines, the endianness of floating point values may not be the same as the endianness of integer values. So, basically, there is no good way to do this portably. bmargulies's recommendation to serialize floating point values as strings for network transit is your best option.
James McNellis
As an FYI, in the future, you should add comments like this as comments to answers or questions by clicking the "add comment" link under each question and each answer.
James McNellis
sorry! as I was new here, I didn't see link for adding a comment. thanks!just for another clarification is required. As I need to exchange dat in binary format, I am trying to use struct. But according to all what you said, it is not a good way. Is there any other way?Thanks again for all your help,mk
make
No problem; it was just a heads-up that you can use that feature. As for a better way to implement the solution, disassembling the floating point value into its component parts, as caf describes, is the cleanest way to do this.
James McNellis
+2  A: 

There's a lot more potential variety and issues in floating point formats than just the endian problem you have to deal with when marshalling and unmarshalling integers.

One way out is to format floating point numbers as text, using printf, and reading them back with strtof() (as bmargulies indicated).

Another way that will work as long as the machines share the same FLT_RADIX value, is to break them into a mantissa and exponent value:

#include <math.h>
#include <limits.h>

float x = 1.13;
int x_exp;
long x_mant;

x_exp = ilogbf(x);
x_mant = (scalbnf(fabsf(x), -x_exp) - 1) * LONG_MAX;
if (x < 0.0)
    x_mant = -x_mant;

You then have an int and a long (x_exp and x_mant from the above code) to put onto the wire, which you can use the normal ntohl() and htonl() functions to do. To convert these back into a float, use:

x = scalbnf((fabsf(x_mant) / LONG_MAX) + 1, x_exp);
if (x_mant < 0)
    x = -x;

Note that most machines have a FLT_RADIX value (defined in float.h) of 2, so if you check for that during compilation and abort if it's something else, you should be reasonably portable.

caf
thank for your helpCould you please give me more information about the second was you described ? Any link that descibes this way of FLT_RADIX value would be very appreciated!thanks again,mk
make
Well, I've provided you with the code (which I just wrote myself, so I don't have any links to point you to). If you do some research on the Sign-Mantissa-Exponent way of representing floating point numbers, it should become clear what the code is doing.
caf
make
int x_rec; float y_rec; // int y_rec_exp; long y_rec_mant; struct { int xx_rec; float yy_rec; } data_rec; //read data from socket if recv(sock1, y_rec_mant=ntohl(data_rec.yy_rec);x = scalbnf((fabsf(y_rec_mant) / LONG_MAX) + 1, y_rec_exp);if (y_rec_mant < 0) y_rec = -y_rec;...Regards and thanks again for all your help,mk
make
No. The `y_snd_exp` and `y_snd_mant` values should both be sent over the wire (after passing through `htonl()`). You also can't just write the `struct` in one pass like that - structs can have padding. You should write each of the values seperately.
caf
Thanks and sorry for disturbing you with itjust for some clarification ..you said that I can't use struct because it can cause padding. But, it is hard to write each of the variables seperately. Is this possible to exchange data in the form of array?Regards and thanks again for all of your help,mk
make
Yes, you can use `memcpy()` to copy the underlying representation of each variable into an array of `char` and send that.
caf
Hi again, I tried to exchange data with FLT_RADIX value and I would like to let you know that I succeded to compile it only on Salaris. But on windows, I get errors regarding 'ilogbf' and 'scalbnf' declaration. I use MSVC++6 for compiling on windows. Is there any library of IEEE functions that should be included? On solaris, I get this warming of converting to `long int' from `float' regarding this line: x_mant = (scalbnf(fabsf(x_snd), -x_exp) - 1) * LONG_MAX;Thanks for all your help, mk
make
The conversion from `long` to `float` is expected - you can add a cast (`fabsf((float)x_snd)`) to eliminate the warning. Those functions are C99, I'm not sure if/when they've been added to Microsoft's implementation. You can rework it to use the `frexp` and `ldexp` functions instead, which are C89 standard.
caf
Thanks again for your help!
make