Puddle
Puddle

Reputation: 3345

union int bits to float bits sometimes interpreted wrong

I just discovered some dodgy problems when i was interleaving some floats. I've simplified the issue down and tried some tests

#include <iostream>
#include <vector>

std::vector<float> v; // global instance

union{ // shared memory space
    float f; // to store data in interleaved float array
    unsigned int argb; // int color value
}color; // global instance

int main(){
    std::cout<<std::hex; // print hexadecimal

    color.argb=0xff810000; // NEED A==ff AND R>80 (idk why)
    std::cout<<color.argb<<std::endl; // NEED TO PRINT (i really dk why)
    v.insert(v.end(),{color.f,0.0f,0.0f}); // color, x, y... (need the x, y too. heh..)

    color.f=v[0]; // read float back (so we can see argb data)
    std::cout<<color.argb<<std::endl; // ffc10000 (WRONG!)
}

the program prints

ff810000
ffc10000

If someone can show me i'm just being dumb somewhere that'd be great.


update: turned off optimizations

#include <iostream>

union FLOATINT{float f; unsigned int i;};

int main(){
    std::cout<<std::hex; // print in hex

    FLOATINT a;
    a.i = 0xff810000; // store int
    std::cout<<a.i<<std::endl; // ff810000

    FLOATINT b;
    b.f = a.f; // store float
    std::cout<<b.i<<std::endl; // ffc10000
}

or

#include <iostream>

int main(){
    std::cout<<std::hex; // print in hex

    unsigned int i = 0xff810000; // store int
    std::cout<<i<<std::endl; // ff810000

    float f = *(float*)&i; // store float from int memory

    unsigned int i2 = *(unsigned int*)&f; // store int from float memory
    std::cout<<i2<<std::endl; // ffc10000
}

solution:

#include <iostream>

int main(){
    std::cout<<std::hex;

    unsigned int i=0xff810000;
    std::cout<<i<<std::endl; // ff810000

    float f; memcpy(&f, &i, 4);
    unsigned int i2; memcpy(&i2, &f, 4);

    std::cout<<i2<<std::endl; // ff810000
}

Upvotes: 0

Views: 226

Answers (2)

Ted Lyngmo
Ted Lyngmo

Reputation: 117688

Writing to the int and then reading from the float in the union causes UB. If you want to create a vector of mixed value types, make a struct to hold them. Also, don't use unsigned int when you need exactly 32 bits. Use uint32_t.

#include <iostream>
#include <vector>

struct gldata {
    uint32_t argb;
    float x;
    float y;
};

std::vector<gldata> v;

int main() {
    std::cout << std::hex; // print hexadecimal

    v.emplace_back(gldata{0xff810000, 0.0f, 0.0f});

    std::cout << v[0].argb << "\n"; // 0xff810000
}

Upvotes: 2

1201ProgramAlarm
1201ProgramAlarm

Reputation: 32727

The behavior you're seeing is well defined IEEE floating point math.

The value you're storing in argb, when interpreted as a float will be a SNaN (Signaling NaN). When this SNaN value is loaded into a floating point register, it will be converted to a QNaN (Quiet NaN) by setting the most significant fraction bit to a 1 (and will raise an exception if floating point exceptions are unmasked).

This load will change your value to from ff810000 to ffc10000.

Upvotes: 3

Related Questions