C union float trick?

Started by
20 comments, last by sit 18 years, 5 months ago
I'm looking through the Quake3 networking code and I come accross this:
void MSG_WriteFloat( msg_t *sb, float f ) {
	union {
		float	f;
		int	l;
	} dat;
	
	dat.f = f;
	MSG_WriteBits( sb, dat.l, 32 );
}
void MSG_WriteChar( msg_t *sb, int c ) {
#ifdef PARANOID
	if (c < -128 || c > 127)
		Com_Error (ERR_FATAL, "MSG_WriteChar: range error");
#endif

	MSG_WriteBits( sb, c, 8 );
}
void MSG_WriteLong( msg_t *sb, int c ) {
	MSG_WriteBits( sb, c, 32 );
}

The function I'm wondering about is MSG_WriteFloat... they declare a union, assign something to the float, and then send the int?! Why do they do this? Wait, I just realized I should look at the read function...
int MSG_ReadLong( msg_t *msg ) {
	int	c;
	
	c = MSG_ReadBits( msg, 32 );
	if ( msg->readcount > msg->cursize ) {
		c = -1;
	}	
	
	return c;
}

float MSG_ReadFloat( msg_t *msg ) {
	union {
		byte	b[4];
		float	f;
		int	l;
	} dat;
	
	dat.l = MSG_ReadBits( msg, 32 );
	if ( msg->readcount > msg->cursize ) {
		dat.f = -1;
	}	
	
	return dat.f;	
}
Again...they assign something to the int, but return the float??? I don't get it. How can they work with floats by using ints in a union variable? Thanks for the help, Darrell
Advertisement
Read this:

CPlusPlus.com tutorial

The portion on unions will explain why.
The read bits and write bits functions take ints (not floats). And with a union all the different possible objects share the same piece of memory (so changing one will change the others). And on the system they're using (x86) sizeof(float)=sizeof(int).

Edit ask if you need a clarification on the above, my description is terse.
Just a warning...according to the C++ Standard assigning one type of a union and then reading another is undefined. Most compilers do what you would probably want/expect, but it would still be perfectly valid for an app to crash then.
This is in fact illegal C++ (not sure about C), however most compilers support it because it's a common trick. The reason is that compilers can optimize by assuming variables of different types cannot alias to the same memory location, but in this case it's fairly easy for the compiler to handle.

Edit: beaten.
The correct C++ way to do this would be to reinterpret_cast it to a float. In C, usually you would use *(int*)&f.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Quote:Original post by Promit
The correct C++ way to do this would be to reinterpret_cast it to a float. In C, usually you would use *(int*)&f.
Are you sure? I thought such reinterpret_casts were implementation defined? Wouldn't the proper method be to use a binary steam's operator <
(continued due to crappy handling of angled brackets in AP posts)
a binary stream's insertion operator? Is there a binary (in the file as text vs binary sense) equivalent of stringstream that could be used to convert POD types to some type of byte buffer?
-Extrarius
Quote:Original post by Promit
The correct C++ way to do this would be to reinterpret_cast it to a float.


Really? In MSVC++7,

int i = 10;float j = reinterpret_cast<float>(i);


gives me:

error C2440: 'reinterpret_cast' : cannot convert from 'int' to 'float'
Quote:Original post by bakery2k1
Quote:Original post by Promit
The correct C++ way to do this would be to reinterpret_cast it to a float.


Really? In MSVC++7,

int i = 10;float j = reinterpret_cast<float>(i);


gives me:

error C2440: 'reinterpret_cast' : cannot convert from 'int' to 'float'


You would use pointers.

This topic is closed to new replies.

Advertisement