C/C++ What does casting do in the low level?

Viewed 343

Somebody told me that type-casting C conversions does only change how the system interprets the information (for example, casting the char 'A' into int does return 65 when using cout to print it since in memory it stays as 01000001).

However, I noticed that, when casting floating point numbers into same width integers, the value is conserved and not changed, as it would be if only the interpretation was changed. For example, let X be a double precision floating point number:

double X = 3.14159;

As far as I now, when inspecting &X we will find (converted by decimal to binary converter):

01000000 00001001 00100001 11111001 11110000 00011011 10000110 01101110

But, as some of you would already know, when doing:

long long Y = (long long)X;

Y will be 3, the truncated version of X, instead of 4614256650576692846, the value it would get when looking at the binary values at &X if looking for a long long.

So, I think it is clear that they were wrong but, then, how does casting work in low level? Is there any detection of whether the value would be changed or not? How would you code it to get Y = 4614256650576692846 instead of Y = 3?

3 Answers

Casting will try to preserve the values as precise as possible. You can use memcpy() to copy bit patterns.

#include <iostream>
#include <cstring>

int main() {
    double X = 3.14159;
    long long Y;
    memcpy(&Y, &X, sizeof(Y));
    std::cout << Y << '\n';
    return 0;
}

Casting lets the compiler decide how to change the data in order for it to be as useful as possible yet respecting the requested datatype.

The int to char conversion just changes the interpretation from, let us say, 65 to 'A'.

However, when we have a value we may want to conserve, the compiler will use special instructions for its conversion.

For example, when casting from double to long long, the processor will use the CVTTSD2SI instruction, which loads and truncates a FP register's value into a general purpose one:

double a = 3.14159;
long long b = (long long)a;

will have a disassembly of (I got rid of the stack pointers for ease of understanding):

movsd   xmm0, QWORD PTR [a]
cvttsd2si       rax, xmm0
mov     QWORD PTR [b], rax

So, the ways to use the original value would be as mentioned in the selected answer: dereferencing the pointer to the double and place it into the long long variable or, as other stated, using memcpy().

If you want to get Y = 4614256650576692846, you can use:

double X = 3.14159;
long long Y = *( (long long*)(&X) );

This will cast a double pointer to a long long pointer, and then the compiler thinks that (long long*)(&X) is somewhere a long long stores.

But I don't advise you to do so because the result is based on how double is stored on your machine, and the result is not guaranteed to be 4614256650576692846.

Related