为什么 reinterpret_cast 只适用于 0?

-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

为什么?我希望后者能1static_cast那样打印。

Eri*_*hil 8

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。这些位代表int1065353216的值(等于 3f800000 16)。

脚注

1 C++ 标准不保证这一点,实际发生的情况取决于其他因素。


eer*_*ika 6

在这两种情况下,程序的行为都是未定义的,因为您通过不引用相同或兼容类型的对象的泛左值访问对象。

您观察到的是一种可能的行为。行为可能会有所不同,但不能保证它会有所不同,但事实并非如此。无论您期望一种结果还是另一种结果,都不能保证会对行为产生影响。

我希望后者像 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 的二进制表示。程序的输出是这些二进制表示与浮点值表示匹配的特定整数,这是否巧合?理论上可以,但可能不是。

  • @MarkRansom 当有人不明白他们的程序表现出未定义的行为时,指出它确实是非常有帮助和有价值的。弄清楚编译器、库、编译器选项等的特定组合“会发生什么”通常会适得其反,因为它会分散人们对行为不可预测的关键信息的注意力。当提问者“必须”理解一个非常简单的观点时,对简单问题给出复杂的答案对人们来说是非常有害的。 (6认同)
  • 完全正确,但无法解释观察到的结果。 (2认同)
  • @MarkRansom 未定义行为是对具有未定义行为的程序的所有行为的解释。 (2认同)
  • @EricPostpischil`未定义的行为并不是对行为的解释。在我看来,这是毫无意义的吹毛求疵。这是对为什么允许这种行为的解释。当然,这种行为的发生是因为编译器生成的内容以及 CPU 执行的内容,但这太平庸了,无需解释。为什么编译器不产生它被允许产生的东西?为什么输出不应该是允许的?基于这些得出任何结论都是毫无意义的,因为这些结论将基于不能保证被重现的东西。 (2认同)