# Thread: strange behavior when using a floating point

1. ## strange behavior when using a floating point

Hi.
I'm currently studying about binary floating point representation, and I encountered something quite confusing while writing some stuff to code.

consider the following case:

Code:
```void show_bytes(byte_pointer start, int len) {
for (int i=0; i<len ; i++)
printf(" %.2x", start[i]);
printf("\n");
}

void show_float(float x) {
show_bytes((byte_pointer) &x, sizeof(float));
}

int main() {
float a=84.75;
show_float(a);
}```
(I know I shouldn't be using C functions here, but the issue here isn't the programming language in particular...)

this will print out the following output:
00 80 a9 42
which is what I expected (from a little-endian machine).

but now when I'm trying the other direction:

Code:
```int main() {
float a=0x42A98000;
printf("%f\n", a);
}```
it'll print the wrong output: 111840608.000000

I even tried to assign the hex digits in different order, i.e:
Code:
```int main() {
float a=0x0080a942;
printf("%f\n", a);
}```
but that didn't work.

what's wrong here?

2. You've not done this very symmetrically -- if you've got to use this byte_pointer thing to look at individual bytes, you've got to use this byte_pointer thing to set individual bytes too.

3. What's wrong here is your expectations of how integral and floating point types are represented. They have different internal representations.

0x0080a942 is an integral constant. The representation of that value in floating point does not have the 4 bytes 0x00, 0x80, 0xa9, 0x42.

That is completely normal. A 4-byte float can represent non-integral values, whereas a 4-byte int (or unsigned) variable can only represent integral values. Common sense says that their internal representation is different.

Your code actually has undefined behaviour, because it is using a type conversion to force the address of a float to be treated as a array of bytes.

4. Thank you both for commenting.

So correct me if I'm wrong:
The constant is treated as an unsigned integer, where 0x0080a942 actually represent:
0000 0000 | 1000 0000 | 1010 1001 | 0100 0010
which resulted to: 16,863,876
and only then, it encode it to floating point number.

BTW, I should've mentioned that the byte pointer is actually
Code:
`typedef unsigned char* byte_pointer;`

Originally Posted by grumpy
Your code actually has undefined behaviour, because it is using a type conversion to force the address of a float to be treated as a array of bytes.
That was made on purpose, this code was written for learning purposes only, so I'm not concerned about undefined behaviour of this type at the moment.

5. Originally Posted by Absurd
That was made on purpose, this code was written for learning purposes only, so I'm not concerned about undefined behaviour of this type at the moment.
Maybe so, but you're taking output values from that code which has undefined behaviour, and expecting to get specific results when you use those output values for something else. That is a logical fallacy.

6. How would you write a simple function to show you the bytes of a variable?

7. Originally Posted by Absurd
How would you write a simple function to show you the bytes of a variable?
Take advantage of the fact that it is well defined to take the address of an object and then cast it to a pointer to char in order to re-interpret the object as a sequence of bytes. Combined in a loop with sizeof, you can then print the numeric value of the bytes of the object.

8. Originally Posted by laserlight
Take advantage of the fact that it is well defined to take the address of an object and then cast it to a pointer to char in order to re-interpret the object as a sequence of bytes. Combined in a loop with sizeof, you can then print the numeric value of the bytes of the object.
Sorry for the ignorance, but how is it different from what I did?

9. Originally Posted by Absurd
Sorry for the ignorance, but how is it different from what I did?
Oh, I see. I think grumpy's claim in post #3 is wrong:
Originally Posted by grumpy
Your code actually has undefined behaviour, because it is using a type conversion to force the address of a float to be treated as a array of bytes.
Admittedly, I made my statement in post #7 based on the assumption that this is C, not C++, because you used C-style I/O and C-style casts, but still think that this holds. The C standard makes this very explicit:
Originally Posted by C99 Clause 6.3.2.3 Paragraph 7d-e
When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
Unfortunately, the C++ standard does not mention the conversion to a pointer to character type in the corresponding section on pointer conversions. On the other hand, it explicitly mentions a way to do what you want, but involving a separate array:
Originally Posted by C++11 Clause 3.9 Paragraph 2
For any object (other than a base-class subobject) of trivially copyable type T, whether or not the object holds a valid value of type T, the underlying bytes making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value. [ Example:
Code:
```#define N sizeof(T)
char buf[N];
T obj;                     // obj initialized to its original value
std::memcpy(buf, &obj, N); // between these two calls to std::memcpy,
// obj might be modified
std::memcpy(&obj, buf, N); // at this point, each subobject of obj of scalar type
// holds its original value```
—end example ]
But I don't think this is necessary: a reinterpret_cast<char*>(&obj) should be fine for examining the bytes of obj. The reinterpret_cast will be equivalent to static_cast<char*>(static_cast<void*>(&obj)), so the question is whether the conversion from void* to char* is well defined, and I believe it is since:
Originally Posted by C++11 Clause 3.9.2 Paragraph 4c
An object of type cv void* shall have the same representation and alignment requirements as cv char*.

10. Originally Posted by laserlight
Oh, I see. I think grumpy's claim in post #3 is wrong:
It's a matter of interpretation. Generally speaking, any attempt to treat an object of one type as if it is an unrelated type (i.e. converting a pointer to X to a pointer to Y, where there is no implicit conversion, and then dereferencing as a Y) yields undefined behaviour.

One could obtain the same effect as the OP without a type conversion using a union
Code:
```//  assume float and unsigned both have sizeof(4), little endian machine, and same floating point representation as in OP
//    I have not validated those assumptions

union Thing
{
float x;
char c[4];
unsigned u;
};

int main()
{
Thing p;
p.x = 84.75;

//   access  p.c to get the individual characters 00 80 a9 42

//   access p.u and print as hex to get 0080a942

p.u = 0x80a942

//   access p.x to get the value 84.75

//   access p.c to get the individual characters

return 0;
}```
This achieves the same effect without the explicit type conversion (with some constraints on what types union members can only be). And both C and C++ standards specify that accessing any union member other than the one assigned gives undefined behaviour.

So what we have is two ways (accessing union members versus type conversion of some sort and dereference) to achieve the same effect. One is specified to have undefined behaviour. The standard specifies some loopholes in which one might potentially consider the other method does not have undefined behaviour (my usage of double-negative is deliberate, as in this context "not undefined" might mean unspecified or implementation-defined - it is a bit hairy to equate "not undefined" with "defined").

Practically, I treat both methods as equivalent. So, irrespective of loopholes, I treat both as undefined - and avoid doing either - unless I'm willing to have non-portable code.

11. Originally Posted by grumpy
Generally speaking, any attempt to treat an object of one type as if it is an unrelated type (i.e. converting a pointer to X to a pointer to Y, where there is no implicit conversion, and then dereferencing as a Y) yields undefined behaviour.
I agree, however, we are not speaking generally. We are speaking about the particular reinterpretation of an object as a sequence of bytes via a conversion from a pointer of the object type to a pointer to a character type.