-3 c++ floating-point types casting
在研究 C++ 中强制转换的行为时,我发现reinterpret_cast
-ing from float*
toint*
仅适用于0
:
float x = 0.0f;
printf("%i\n", *reinterpret_cast<int*>(&x));
Run Code Online (Sandbox Code Playgroud)
打印0
,而
float x = 1.0f;
printf("%i\n", *reinterpret_cast<int*>(&x));
Run Code Online (Sandbox Code Playgroud)
打印1065353216
。
为什么?我希望后者能1
像static_cast
那样打印。
Areinterpret_cast
表示重新解释其操作数的位。在您的示例中,操作数 ,&x
是 a 的地址float
,因此它是 a float *
。reinterpret_cast<int *>
要求将这些位重新解释为int *
,因此您得到int *
. 如果指向int
和指向的指针float
在您的 C++ 实现中具有相同的表示形式,则这可能1为您提供int *
指向float
.
但是,reinterpret_cast
不会更改指向的位。他们仍然拥有相同的价值观。在float
值为 0的情况下,用于表示它的位都是零。当您通过取消引用访问它们时int *
,它们将被读取并解释为int
. 全为零的位表示零int
值。
在float
值为 1的情况下,用于在 C++ 实现中表示它的位是,使用十六进制来显示它们, 3f800000 16。这是因为格式的指数字段是用偏移量存储的,所以有一些非零位来显示指数的值。(这是浮点格式编码方式的一部分。从概念上讲,1.0f
表示为 + 1.00000000000000000000000 2 • 2 0. 然后是 + 号和“1”之后的位。字面上存储为零位。但是,指数是通过添加 127 并将结果存储为八位整数来存储的。因此 0 的指数存储为 127。指数的表示值为零,但表示它的位不为零。)当您通过取消引用访问这些位时int *
,它们被读取并解释为int
。这些位代表int
1065353216的值(等于 3f800000 16)。
1 C++ 标准不保证这一点,实际发生的情况取决于其他因素。
在这两种情况下,程序的行为都是未定义的,因为您通过不引用相同或兼容类型的对象的泛左值访问对象。
您观察到的是一种可能的行为。行为可能会有所不同,但不能保证它会有所不同,但事实并非如此。无论您期望一种结果还是另一种结果,都不能保证会对行为产生影响。
我希望后者像 static_cast 一样打印 1 。
期望 reinterpret_cast 表现得像 static_cast 那样是不合理的。它们大不相同,一个不能代替另一个。使用 static_cast 转换指针会使程序格式错误。
除非知道它的作用并且知道它的使用是正确的,否则不应使用raininterpret_cast。实际用例很少。
以下是一些具有明确定义的行为的示例,并且保证打印 1:
int i = x;
printf("%i\n", i);
printf("%i\n", static_cast<int>(x));
printf("%g\n", x);
printf("%.0f\n", x);
Run Code Online (Sandbox Code Playgroud)
鉴于我们已经得出结论,行为是未定义的,因此无需进一步分析。
但是我们可以考虑为什么行为可能恰好是我们观察到的。然而,重要的是要理解,当行为未定义时,这些考虑将无助于控制结果。
1.0f 和 +0.0f 的 32 位 IEEE 754 浮点数的二进制表示恰好是:
0b00111111100000000000000000000000
0b00000000000000000000000000000000
Run Code Online (Sandbox Code Playgroud)
这也恰好是整数 1065353216 和 0 的二进制表示。程序的输出是这些二进制表示与浮点值表示匹配的特定整数,这是否巧合?理论上可以,但可能不是。