tags:

views:

151

answers:

3

This is in C, but I tagged it C++ incase it's the same. This is being built with: Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.220 for 80x86 if that makes any different

Why does this work?

(inVal is 0x80)

float flt = (float) inVal;
outVal = *((unsigned long*)&flt);

(results in outVal being 0x43000000 -- correct)

But this doesn't?

outVal = *((unsigned long*)&((float)inVal));

(results in outVal being 0x00000080 -- NOT CORRECT :( )

Before asking this question I googled around a bit and found this function in java that basically does what I want. If you're a bit confused about what I'm trying to do, this program might help explain it:

class hello
{
    public static void main(String[] args)
    {
        int inside = Float.floatToIntBits(128.0f);
        System.out.printf("0x%08X", inside);
    }
}
+8  A: 

You're trying to take the address of a non-const temporary (the result of your (float) conversion) – this is illegal in C++ (and probably also in C). Hence, your code results in garbage.

In your first, working, code, you're not using a temporary so your code is working. Notice that from a standards point of view this is still ill-defined since the size and internal representation of the involved types isn't specified and may differ depending on platform and compiler. You're probably safe, though.

Konrad Rudolph
I wouldn't call it "ill-defined." The behavior is well-defined; it just isn't portable. "Ill-advised" maybe (but sometimes necessary).
Michael Carman
Not well-defined, actually. For starters, if sizeof(unsigned long) > sizeof(float), the dereference is reading outside the float. There may also be alignment differences, which would cause the dereference to fail. And finally there may be trap bits in both floats and longs.
MSalters
+2  A: 

In C99, you may use compound literals to make this work inline:

unsigned long outVal = *((unsigned long *)&((float){ inVal }));

The literal (float){ inVal } will create a variable with automatic storage duration (ie stack-allocated) with a well-defined address.

Type punning may also be done using unions instead of pointer casts. Using compound literals and the non-standard __typeof__ operator, you can even do some macro magic:

#define wicked_cast(TYPE, VALUE) \
    (((union { __typeof__(VALUE) src; TYPE dest; }){ .src = VALUE }).dest)

unsigned long outVal = wicked_cast(unsigned long, (float)inVal);

GCC prefers unions over pointer casts in regard to optimization. This might not work at all with the MS compiler as its C99 support is rumored to be non-existant.

Christoph
I won't be able to test this until tomorrow, but these both look very much like what I want in order to make a macro for this. We'll see if they both work on MSCC or not :)Thanks
Pod
Yes, MSVC doesn't bother with C99 at all (Microsoft seems to assume that everybody uses C++). I just tested VS08, and this fails to compile.
ephemient
I tried this myself and it fails. I was hoping to have wicked_casts everywehere :(
Pod
A: 

Assuming: inVal and outVal are parameters.

void func(int inVal,unsigned long* outVal)
{
    float flt = (float) inVal;

    *outVal = (unsigned long)flt; // convert flot to unsigned long.
                                  // Then assign to the variable by de-ref
                                  // the pointer.
}
Martin York
You'll have problems with strict aliasing. Optimized code will behave differently because of that: http://www.cellperformance.com/mike_acton/2006/06/understanding_strict_aliasing.html
HMage
@HMage. You really need to read the article more carefully, you obviously have no idea what you are talking about. The only difference between my code and the original is that I do it in two lines rather than one (because it makes it more readable). The compiler will have no harder time optimizing that than anything else.
Martin York