1

I was investigating the structure of floating-point numbers, and I've found that most of compilers use IEEE 754 standard to store floating point numbers. And when I tried to do:

float a=0x3f520000; //have to be equal to 0.8203125 in IEEE 754
printf("value of 'a' is: %X [%x] %f\n",(int)a,(int)a, a);

it produces the result:

value of 'a' is: 3F520000 [3f520000] 1062338560.000000

but if I try:

int b=0x3f520000;
float* c = (float*)&b;
printf("value of 'c' is: %X [%x] %f\r\n", *(int*)c, *(int*)c, c[0]);

it gives:

value of 'c' is: 3F520000 [3f520000] 0.820313

The second try gave me the right answer. What is it wrong with the first try? And why does the result differ from that when I cast int to float via pointer?

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
Sultan
  • 23
  • 4
  • Welcome to SO. In your first code you assign an integer literal of value 1062338560 to a float variable. That is probably not what you want to do. Such an assignment does not apply any encoding style defined by IEEE 754 but only converts an integer to a float of same value. – Gerhardh Apr 27 '18 at 10:38
  • 1
    as a side note, IEE-754 representations of `float` and `double` are not guaranteed by the standard. –  Apr 27 '18 at 10:40
  • I hope you will agree that `float a = 1062338560` should assign 1062338560 to `a`, not any other value. So `float a = 0x3f520000` does exactly the same thing. Normal operations on floating-point values always operate on the *values*, not on the internal bit-by-bit mantissa-and-exponent structure -- because of course working with the values is what you want the vast majority of the time. If you want to work with the internal, bit-by-bit structure, you must use other techniques, such as your dodge involving `int b` and casts and pointers. – Steve Summit Apr 27 '18 at 10:41
  • 1
    Hexadecimal floating point constant values are handled differently in C and C++. Pick *one* language. – Some programmer dude Apr 27 '18 at 10:41
  • Thank you all very much! You've answered so quick! It makes me feel great) – Sultan Apr 27 '18 at 10:55
  • 1
    Apart from the representation being different, this is also a [strict aliasing violation](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). – Lundin Apr 27 '18 at 11:00

4 Answers4

4

[Note: This answer assumes C, not C++, which have different rules]

With

float a=0x3f520000;

you take the integer value 1062338560 and the compiler will convert it to 1062338560.0f.

If you want hexadecimal floating point constant you must use exponent-format using the letter p. As in 0x1.a40010c6f7a0bp-1 (which is the hexadecimal notation for 0.820313).

What happens with

int b=0x3f520000;
float* c = (float*)&b;

is that you break strict aliasing and tell the compiler that c is pointing to a floating-point value (the strict aliasing break is because b isn't a floating point value). The compiler will then reinterpret the bits in *c as a float value.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    Rather: the strict aliasing violation would mean that further code like `*c = 3.14f; printf("%x", b);` might print `0x3f520000` instead of the floating point representation of `3.14f`, because of the invoked undefined behavior. – Lundin Apr 27 '18 at 11:03
4

The difference is that the first converts the value (0x3f520000 is the integer 1062338560), and is equivalent to this:

float a = 1062338560;
printf("value of 'a' is: %X [%x] %f\n",(int)a,(int)a, a);

The second reinterprets the representation of the int - 111111010100100000000000000000 - as being the representation of a float instead.
(It's also undefined, so you shouldn't expect it to do anything in particular.)

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • Thank you! I didn't think that I'm assigning an integer value to a float. I've realized that now) – Sultan Apr 27 '18 at 10:51
1

0x3f520000 is an integer constant. When assigned to a float, the integer is converted.

ROCFER
  • 267
  • 2
  • 9
0

Some more proper example of how to convert in the second case:

#include <stdio.h>
#include <string.h>
#include <stdint.h>

int main() {
  uint32_t fi = 0x3f520000;
  float f;
  memcpy(&f, &fi, sizeof(f));
  printf("%.7g\n", f);
}

it prints:

0.8203125

so that is what you expected.

The approach I used is memcpy that is the safest for all compilers and best choice for modern compilers (GCC since approx. 4.6, Clang since 3.x) that interpret memcpy as "bit cast" in such case and optimize it in a efficient and safe way (at least in "hosted" mode). That's still safe for older compilers, but not nessessarily efficient in the same way; some can prefer cast through union or ever through different pointer type. On dangers of that ways, see here or generally search "type punning and strict aliasing".

(Also, there could be some weird platforms that suffer from endianness issue that integer endianness differs from float one; ones that have byte different than 8 bits, and so on. I don't consider them here.)

UPDATE: I was starting answering to initial version of the question. Yep, bit casting and value conversion will give principally different results. That's how float numbers work.

Netch
  • 4,171
  • 1
  • 19
  • 31