views:

483

answers:

6

I have a unsigned long integer value which represents a float using IEEE-754 format. What is the quickest way of printing it out as a float in C++?

I know one way, but am wondering if there is a convenient utility in C++ that would be better.

Example of the way that I know is:

union
{
    unsigned long ul;
    float f;
} u;

u.ul = 1084227584; // in HEX, this is 0x40A00000

cout << "float value is: " << u.f << endl;

(This prints out "float value is: 5" )

+3  A: 

EDIT: I previously thought this was "just nasty", but it turns out to be worse than I thought - see the comments for details. I wouldn't recommend this approach, but I'm leaving it here to document the possibility (and the caveat!)


You can use pointers:

long ul = 1084227584;
float* fp = (float*) &ul;
float f = *fp;

(This can be condensed into a single line, but I think it's clearer not to.)

Basically the same thing as your union... and equally dodgy unless you're sure of the sizes of the types involved.

If your platform supports them, you should probably use the size-specific type names for both the integer and floating point types.

Jon Skeet
Actually, it is implementation dependent whatever the sizes. This is better than the union trick, which is undefined, but still not good.
anon
Bad idea. This breaks the C99 strict aliasing rules, and an aggressive optimizing compiler could produce the incorrect result. See http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html .
Adam Rosenfield
Thanks - I'd thought it was unpleasant, but not actually potentially broken when the sizes were okay. Edited to make this clearer, thanks. Please let me know if you think it's such a bad idea that the answer should be deleted completely - I'd be happy to oblige.
Jon Skeet
I'd leave it in -- type-punning with pointers is a common technique despite its wrongness. Many compilers don't follow the strict aliasing rules (heck, there isn't even a single fully C99-compliant compiler 10 years after the standard), but it's still worth knowing about.
Adam Rosenfield
Just to be really pedantic: the new "strict aliasing rules" in that sense are rules for programmers, not rules for compilers. All compilers *follow* the rules, it's just that most compilers don't *take advantage* of them to apply all the optimisations they could. But you can be a standard-compliant compiler just by assuming that any two pointers, of any types, might be aliases.
Steve Jessop
+2  A: 

It will only work on 32-bit machines or machines where sizeof(long) == sizeof(float). On modern machines you might want to use int instead ... and you really have to take care if you're performing this trick.

arhuaco
A: 

If you were to use printf, it would look like this:

printf("%f", u.ul);
warren
No -- floats get implicitly converted to doubles when passed to variadic functions like `printf`, and so the `%f` format specifier expects an 8-byte double, not a 4-byte unsigned long. You're going to get garbage.
Adam Rosenfield
Thanks, Adam - was not aware of that
warren
+3  A: 

I was thinking the same thing as Jon Skeet, but he beat me to it. However, I would do it a little more concisely than he did.

cout << "float value is: " << *((float *) &ulValue)) << endl;

You get a pointer to your unsigned long, then reinterpret that as a pointer to float, then dereferencing your pointer to float produces the float value.

Since you are doing this in C++, it is clearer to use reinterpret_cast instead of the old C-style cast.

cout << "float value is: " << *(reinterpret_cast<float *>(&ulValue)) << endl;
A. Levy
+5  A: 

The union method you suggested is the usual route that most people would take. However, it's technically undefined behavior in C/C++ to read a different member from a union than the one that was most recently written. Despite this, though, it's well-supported among pretty much all compilers.

Casting pointers, as Jon Skeet suggested, is a bad idea -- that violates the strict aliasing rules of C. An aggressive optimizing compiler will produce incorrect code for this, since it assumes that pointers of types unsigned long * and float * will never alias each other.

The most correct way, in terms of standards compliance (that is, not invoking undefined behavior), is to cast through a char*, since the strict aliasing rules permit a char* pointer to alias a pointer of any other type:

unsigned long ul = 0x40A00000;
float f;
char *pul = (char *)&ul;  // ok, char* can alias any type
char *pf = (char *)&f;    // ok, char* can alias any type
memcpy(pf, pul, sizeof(float));

Though honestly, I would just go with the union method. From the cellperformance.com link above:

It is an extremely common idiom and is well-supported by all major compilers. As a practical matter, reading and writing to any member of a union, in any order, is acceptable practice.

Adam Rosenfield
Wow, I didn't realize that aliasing would cause problems. Thanks for sharing!
A. Levy
Would it be acceptable to use void* instead of char*?
A. Levy
+1  A: 

swegi had the right direction, but missed one character. The correct way is

long l = 1084227584L
float f = reinterpret_cast<float&>(l);
MSalters