C/C++中转换(casting)在低层级中有什么作用?

4
有人告诉我,C语言中的类型转换只会改变系统对信息的解释方式(例如,将字符型 'A' 转换为整型,使用 cout 输出该值时会返回 65,因为在内存中它仍保持为01000001)。
然而,我注意到当将浮点数强制转换为相同位数的整数时,其值是保留不变的,而不仅仅是改变了解释方式所导致的结果。例如,假设有一个双精度浮点数 X:
double X = 3.14159;

据我所知,在检查&X时,我们将发现(通过十进制转二进制转换器进行转换):
01000000 00001001 00100001 11111001 11110000 00011011 10000110 01101110

但是,正如你们中的一些人可能已经知道的那样,当进行以下操作时:

long long Y = (long long)X;

Y将会是X的截断版本3,而不是4614256650576692846。如果查找&X的二进制值作为long long,则会得到后者的值。

所以,我认为这很明显是错误的,那么,在低级别中转换是如何工作的呢?是否有检测是否更改值的功能?如何编码以获取Y = 4614256650576692846而不是Y = 3


2
所有转换都是不同的。这个 转换会改变比特位。 - user253751
1
memcpy(&Y, &X, sizeof(Y)); - MikeCAT
1
每当你感觉需要在C++程序中进行C风格的类型转换时,应该将其视为你正在做某些错误的迹象。 - Some programmer dude
charint的大小(几乎总是)不同,因此这不是编译器重新解释位的最佳示例。在常见情况下,编译器必须在将char解释为int之前逻辑上添加三个字节的数据。如果char为负,则这些字节甚至可能不全为0。将该转换描述为创建具有与char相同数值的int更简单。 - chris
强制类型转换是无关紧要的。long long Y = X;会做完全相同的事情。 - Pete Becker
3个回答

4

类型转换将尽可能地保留值的精度。

您可以使用 memcpy() 来复制位模式。

#include <iostream>
#include <cstring>

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

只是一些小问题:不能保证 sizeof(long long) == sizeof(double)。现在只是碰巧这样。此外,处理位时,请优先使用“无符号”整数。 - Some programmer dude

3
转换让编译器决定如何更改数据,以使其尽可能有用,但仍尊重所请求的数据类型。
将int转换为char只是改变了解释方式,例如从65到'A'。
然而,当我们有一个值需要保留时,编译器将使用特殊指令进行转换。
例如,从double转换为long long时,处理器将使用CVTTSD2SI指令,该指令将FP寄存器的值加载并截断到通用寄存器中。
double a = 3.14159;
long long b = (long long)a;

以下是反汇编代码(为了易于理解,我去掉了堆栈指针):

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

因此,使用原始值的方法如选定答案中所述:取消引用指向double的指针并将其放入long long变量中,或者像其他人所述使用memcpy()。


0
如果你想要得到Y = 4614256650576692846,你可以使用以下代码:
double X = 3.14159;
long long Y = *( (long long*)(&X) );

这将把一个双指针强制转换为long long指针,然后编译器认为(long long*)(&X)是存储long long的位置。

但我不建议你这样做,因为结果取决于你的机器上如何存储double,结果不能保证是4614256650576692846。


2
不仅如此,这种类型转换是未定义的行为。您不能使用long long别名double。(在大多数情况下,只允许基于char的类型别名。)bit_cast可以解决这个问题,同时看起来更好。 - chris
你需要使用memcpy来安全、可移植地进行类型转换。或者使用C++20的std::bit_cast。这个答案中的代码只在某些实现中是安全的,比如g++ -fno-strict-aliasing,或者始终使用MSVC。但是这个问题标记了g++和clang,所以MSVC特定的类型转换不是一个好答案,特别是没有警告它在ISO C++中是UB,并且是一个非标准扩展,在大多数编译器中并不完全安全。(它可能在简单情况下工作,但是memcpy总是有效的,并且在现代编译器中可以高效优化。) - Peter Cordes
1
这是糟糕的代码。该答案违反了严格别名规则。请参阅“什么是严格别名规则?”(https://dev59.com/43VD5IYBdhLWcg3wE3Ro)。它还可能违反C11标准的6.3.2.3指针第7段中所述的条款(其他版本和C++也有类似的限制):“对于对象类型的指针可以转换为不同对象类型的指针。如果生成的指针未正确对齐以引用类型,则行为是未定义的。” - Andrew Henle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接