Techniques for reinterpreting the bits of one type as a different type

-1

Note: This question started with a faulty premise: the values that appeared to be 0.0 were in fact very small numbers. But it morphed into a discussion of different ways of reinterpreting the bits of one type as a different type. TL;DR: until C++20 arrives with its new bit_cast class, the standard, portable solution is memcpy.

Update 3: here's a very short app that demonstrates the problem. The bits interpreted as float always give a value of 0.0000:

#include <iostream>
int main() {

    uint32_t i = 0x04501234;  // arbitrary bits should be a valid float
    uint32_t bitPattern;
    uint32_t* ptr_to_uint = &bitPattern;

    volatile float bitPatternAsFloat;
    volatile float* ptr_to_float = &bitPatternAsFloat;

    do {
        bitPattern = i;

        memcpy( (void*)ptr_to_float, (const void*)ptr_to_uint, 4 );

        // The following 2 lines both print the float value as 0.00000
        //printf( "bitPattern: %0X, bitPatternAsFloat: %0X, as float: %f \r\n", bitPattern, *(unsigned int*)&bitPatternAsFloat, bitPatternAsFloat );
        printf( "bitPattern: %0X, bitPatternAsFloat: %0X, as float: %f \r\n", bitPattern, *(unsigned int*)&bitPatternAsFloat, *ptr_to_float );

        i++;

    } while( i < 0x04501254 );

    return 0;
}

(original post)

float bitPatternAsFloat; 
for (uint32_t i = 0; i <= 0xFFFFFFFF; i = (i & 0x7F800000) == 0 ? i | 0x00800000 : i + 1 ) 
    {
        bitPatternAsFloat = *(float*)&i;
...

The loop steps through every bit pattern, skipping those in which the float exponent field is 0. Then I try to interpret the bits as a float. The values of i look OK (printed in hex), but bitPatternAsFloat always comes back as 0.0. What's wrong?

Thinking that maybe i is held in a register and thus &i returns nothing, I tried copying i to another uint32_t variable first, but the result is the same.

I know I'm modifying the loop variable with the ternary in the for statement, but as I understand it this is OK.

Update: after reading about aliasing, I tried this:

union FloatBits { 
    uint32_t uintVersion;
    float floatVersion;
};
float bitPatternAsFloat;

// inside test method: 
union FloatBits fb;  // also tried it without the 'union' keyword

// inside test loop (i is a uint32_t incrementing through all values)
fb.uintVersion = i;
bitPatternAsFloat = fb.floatVersion;

When I print the values, fb.uintVersion prints the expected value of i, but fb.floatVersion still prints as 0.000000. Am I close? What am I missing? The compiler is g++ 6, which allows "type punning".

Update 2: here's my attempt at using memcpy. It doesn't work (the float value is always 0.0):

uint32_t i = 0;
uint32_t bitPattern;
float bitPatternAsFloat;

uint32_t* ptr_to_uint  = &bitPattern;
float*    ptr_to_float = &bitPatternAsFloat;

do {
    bitPattern = i; //incremented from 0x00000000 to 0xFFFFFFFF
    memcpy( (void*)ptr_to_float, (const void*)ptr_to_uint, 4 );
    // bitPatternAsFloat always reads as 0.0
    ...
    i++;
    } while( i );
c++
pointers
casting
asked on Stack Overflow Aug 3, 2018 by Robert Lewis • edited Aug 5, 2018 by Robert Lewis

2 Answers

0

Thanks to @geza for pointing out that the "failures" were an artifact of printf not being able to show very small numbers in %f format. With %e format the correct values are displayed.

In my tests, all of the various conversion methods discussed work, even the undefined behavior. Considering simplicity, performance, and portability it seems the solution using a union is best:

uint32_t bitPattern; 
volatile float bitPatternAsFloat;

union UintFloat {
    uint32_t asUint;
    float asFloat;
} uintFloat;

do {
    bitPattern = i;  // i is loop index
    uintFloat.asUint = bitPattern;                               
    bitPatternAsFloat = uintFloat.asFloat;

An interesting question is whether the float member in the union should be declared volatile: it is never explicitly written, so might the compiler cache the initial value and reuse it, failing to notice that the corresponding uint32_t is being updated? It appears to work OK on my current compiler without the volatile declaration, but is this a potential 'gotcha'?

P.S. I just discovered [reinterpret_cast][1] and wonder if that's the best solution.

answered on Stack Overflow Aug 4, 2018 by Robert Lewis • edited Aug 4, 2018 by Robert Lewis
-1

Your code involves undefined behavior. The standard does not put any restriction on the behavior of codes that involve UB (it can work, it can crash, it can turn your computer into a princess, or anything the compiler loves to do). In standard C++, there is no portable way to reinterpret the bits of a uint32_t as a float. Your approach does not do this as well; it just involves UB. The "other code" you mentioned are probably UB too; maybe they're just lucky enough to work.

You can use an external library, though, if it does work. (Or you can use another language :D)

answered on Stack Overflow Aug 3, 2018 by L. F.

User contributions licensed under CC BY-SA 3.0