T. Lau
T. Lau

Reputation: 39

casting from unsigned int to float

I have a 32 bit unsigned value that I want to convert to ieee 754 floating point.

Here are my variables:

unsigned int val = 0x3f800000;  
float floatVal;

I know that I have to do the following to be able to properly cast the uint value to the ieee 754 floating point value I expect of 1.0

floatVal = *((float*)&val);

Can anyone explain why floatVal = (float)val does not return the same result? Instead of 1.0 floatVal gets a value of 1.0653532e+009. I believe it is partially due to the fact that bits are lost during this type of typecasting whereas the bits are preserved through pointer casting but a more detailed explanation would be much appreciated.

Upvotes: 2

Views: 13686

Answers (1)

Nominal Animal
Nominal Animal

Reputation: 39308

  1. In current C standards (C99, C11), one should type-pun via an union, rather than dereferencing a type-cast pointer:

    #include <stdint.h>
    
    uint32_t  float_bits(const float  f)
    {
        union {
            uint32_t  u;
            float     f;
        } temp;
    
        temp.f = f;
        return temp.u;
    }
    
  2. Most current architectures use IEEE-754 binary32 format for float type, but not all. If your program assumes this, it should say so in the documentation.
    I personally like to check it at compile time, and fail the compilation if my code cannot cope.

  3. Most current architectures use the same byte order (endianness) for integer and float types, but not all. Again, if your program assumes this, it should say so in the documentation, or check it at compile time (using a separate test program in the same project, for example).

  4. In C, the expression (type)variable casts the value of variable variable to type type. For example:

    int32_t my_truncate(float value)
    {
        return (int32_t)value;
    }
    

    If for example value == 2.125, then my_truncate(value) == 2.

    Similarly, casting an integer value to a floating-point type, evaluates to a floating-point value that best represents the original integer value. For example, (float)425 == 425.0f. (The final f just tells the value is of float type. If there is no f at the end of a floating-point value, its type in C is double.)

  5. "Storage representation" refers to how values are actually stored in memory.

  6. The difference between casting and type-punning is that casting reinterprets the value itself, but type-punning reinterprets how the storage representation of the value is interpreted.

    Thus, casting an integer value to a float type just yields a float that best represents the original integer value; but type-punning an integer value (of the same size as a float) to float means the storage representation of that integer value is treated as the storage representation of a float value, yielding the float value that has the same storage representation as the original integer value has.

    Similarly in the other direction. Type-punning a float to an unsigned integer type, will yield the unsigned integer (and thus bits) that has the same storage representation as the original float had. This is exactly what the float_bits() example function above does.

  7. In C99 and later versions of the C standard, as well as in POSIXy C implementations, you can use the frexpf() function to split a float into a normalized fraction: x × 2n, where either -1.0 < x <= -0.5f or 0.5 <= x < 1; and n is an integer exponent.
    If need be, one can use this to construct the representation of the closest value binary32 can represent, in an uint32_t. This can be useful on architectures that does not use IEEE 754 binary32 for floats.

Upvotes: 11

Related Questions