float f = 12.5;
unsigned int _f = *reinterpret_cast<int*>(&f);
std::cout << _f << std::endl; // result: 1095237632
有人可以解释一下这种转换的工作方式吗? _f
代表什么?
编辑
因此在转换为二进制后我得到的这个数字1095237632
是0b1000001010010000000000000000000
,而在IEEE-754中这个二进制数字是12.5
。我正确吗?
答案 0 :(得分:6)
没人能解释(*),因为它不起作用。
来自cppreference:
与static_cast不同,但与const_cast不同,reinterpret_cast 表达式不能编译为任何CPU指令(除非 在整数和指针之间或在晦涩的体系结构上进行转换 指针表示取决于其类型)。纯粹是 编译时指令,指示编译器处理 表达式,就好像其类型为new_type。
只能通过reinterpret_cast进行以下转换, 除非这种转换会消除稳定性或波动性。
然后遵循一系列规则,涵盖允许重新解释强制类型转换的内容。将类型A
转换为完全不相关的类型B
不在其中,并且您的代码表现出不确定的行为。
(*)严格来说是不正确的。您将float视为一个int,如果您查看它们在硬件上的表示形式,并且检查编译器的输出,则可以弄清楚为什么得到值,尽管未定义行为是未定义的,因此不值得输入详细信息除非您愿意编写不可移植的代码。
答案 1 :(得分:4)
让我们看一下两个功能。其中之一强制转换为定期将其转换为int,其中之一使用reinterpret_cast重新解释其强制转换:
int cast_to_int(float f) {
// Rounds f to an int, rounding towards 0
return (int)f;
}
int reinterpret_cast_to_int(float i) {
// Just copies the bits
return *reinterpret_cast<int*>(&i);
}
那么实际发生了什么?让我们看一下程序集:
cast_to_int(float):
cvttss2si eax, xmm0 // We cast to an int
ret
reinterpret_cast_to_int(float):
movd eax, xmm0 // We directly copy the bits
ret
在第一种情况下,有一条汇编指令执行转换:
cast_to_int(0.7) -> 0
cast_to_int(1.0) -> 1
cast_to_int(1.5) -> 1
cast_to_int(2.1) -> 2
在第二种情况下,reinterpret_cast
只是直接转换位的基础表示形式。它实际上并没有执行任何操作,该函数只是将输入寄存器复制到输出寄存器。
在幕后,float
的位表示形式与int
截然不同,这就是为什么您得到奇怪的数字的原因。
答案 2 :(得分:-4)
如下注释所示,对于不相关的类型,使用reinterpret_cast ist不安全。因此,请勿以这种方式使用它。
首先,应避免将int分配给uint。
对于x86 / x64系统,float通常表示为4个字节,例如uint32_t,并且它们的对齐方式基本上相同。
许多编译器允许以下操作: uint32_t _f = * reinterpret_cast(&f);
但是由于某些原因,这会导致不确定的行为(感谢评论): -优化 -对齐 -...
使用memcpy代替。
如果对齐方式相同并且值存储在内存中,则以下效果描述了使用reinterpret_cast时发生的情况:
4个字节浮点数的存储位置为&f。通过将重新解释转换为uint32_t,此内存将重新解释为uint32。取消引用的值_f包含与浮点f相同的字节,但解释为uint32。 您可以将其还原并获得原始值12.5:
float f = 12.5;
uint32_t _f = *reinterpret_cast<uint32_t*>(&f);
float _fnew = *reinterpret_cast<float*>(&_f);
std::cout << _fnew << std::endl; // result: 12.5
reinterpret_cast仅重新解释存储位置(地址)。 如果对齐方式不相同,或者将值存储在寄存器中,进行了优化等等,则强制转换会导致未定义的值。