Why is output of this float union NaN?

2019-02-23 19:20发布

问题:

In my C++ code I have declared a union:

typedef union U_FloatParse {
    float float_data;
    unsigned char byte_data[4];
} U_FloatConvert;

Then, I set the byte_data array to the values 0, 0, 192, 127:

U_FloatConvert depth_data;

depth_data.byte_data[0] = 0;
depth_data.byte_data[1] = 0;
depth_data.byte_data[2] = 192;
depth_data.byte_data[3] = 127;

printf("\n\nFloat = %f\n\n", depth_data.float_data);

As output I get NaN. Why don't I get a normal float value? When I pass the values: 56, 137, 33, 63. I get the float value 0.631000.

Test code: http://codepad.org/Q8ds1V0F

回答1:

By writing the said bytes into the array, you get a memory layout of 00 00 c0 7f, which, in little endian, reads as 7fc00000. Interpreting this as an IEEE float gives you

  • a sign of 0 (positive, but irrelevant for NaN)
  • an exponent of 0xFF
  • a mantissa of 0x400000 (> 0 and has the most significant bit set → silent)

which gets you a (silent) NaN value.

At least, this counts for C. In C++, it is not defined what you do, but if you nevertheless get NaN as a result, your compiler treats it if it was C (not recommended nevertheless to rely on undefined behaviour!).



回答2:

NaN is a normal float value, and the bytes you set are correctly interpreted as a valid NaN.

That being said, you could have got the output "there is no spoon NaN", since using a union in this manner has undefined behaviour in C++. You may only use one of the union's members at any given time. It's not a shortcut for reinterpret-casting!



回答3:

NaN(not a number) error generally occurs if the ouput is indeterminate i.e whose value couldn't be determined such as 0/0.

But I wouldn't use unions anyway (personally don't like them), when you use a union, it is undefined behavior to set one member and extract a different member.

For one thing, floating-point values are basically split into bit fields, with so many bits holding the mantissa, so many holding the exponent, and one holding the sign. And integer, on the other hand is simply a base-2 representation of a number. Their bits just aren't arranged the same.

You could do like this:

unsigned long int l = byte_data[0] | (byte_data[1] << 8) | (byte_data[2] << 16) | (byte_data[3] << 24);