当指向 32 位整数的指针被强制转换为指向浮点数的指针并取消引用时,会发生什么情况?

What happens when a pointer to a 32-bit integer is cast to a pointer to a float and dereferenced?

提问人:RookieCPP 提问时间:12/18/2022 最后编辑:user3840170RookieCPP 更新时间:12/19/2022 访问量:502

问:

我看到了这个演员表:

const std::uint32_t tmp = (some number);
float ToFloat = *(float*)(&tmp);

但是在投掷时,错误的数字会进入浮点数。此外,如果我使用静态强制转换,那么它是正确强制转换的。

  1. 为什么会这样?
  2. static_cast究竟是如何工作的?
  3. *(float*)(&Tmp)意味着我们正在尝试取消引用指向浮点数的指针,浮点数位于地址 .这是对的吗?&tmp
C++ 强制转换 浮点 类型双关语

评论

0赞 Pepijn Kramer 12/18/2022
无论如何,在 C++ 程序中“C”样式的转换在做什么?你所看到的不建议使用:所以我很好奇你在哪里见过它?因为它似乎不是学习 C++ 的好参考auto value = static_cast<float>(tmp);
0赞 fabian 12/18/2022
第二条语句实际上确实如此,即你告诉编译器忽略它所知道的关于表达式()的实际类型的任何内容,而只是将指针变成指向的指针。当然,指针所引用的数据实际上并不是浮点数,结果是未定义的行为。不过,只需从 ing 到 即可让编译器对值进行适当的转换。float ToFloat = *reinterpret_cast<float*>(const_cast<uint32*>(&Tmp));&Tmpuint32 const*floatstatic_castuint32float
1赞 UnholySheep 12/18/2022
这看起来像是从快速平方反比根直接复制的——它被用于一个非常特定的目的(并且已经过时了 20 多年)
2赞 john 12/18/2022
目的不是在浮点数中获取正确的值(如您所说,简单的强制转换可以做到这一点),而是直接将位模式从 分配给 。大概具有与所需浮点数相对应的位。因此,要理解代码,您需要了解浮点值如何存储为位模式。TmpToFloatTmp

答:

2赞 Ted Lyngmo 12/18/2022 #1

为什么会这样?

该程序具有未定义的行为,因为您通过 .这在 C++ 中是不允许的。intfloat

static_cast究竟是如何工作的?

在这种情况下,它执行的转换与执行的转换相同。也就是说,它将 转换为 并将其分配给 。float ToFloat = Tmp;intfloatToFloat

*(float*)(&Tmp)意味着我们正在尝试取消引用指向浮点数的指针,浮点数位于地址 &Tmp。这是对的吗?

没有 at ,只有一个 .但是,您可以告诉编译器指针指向 并取消引用该指针。s 和 s 的位模式非常不同,因此它不仅具有未定义的行为,而且不太可能获得正确的结果。float&Tmpintfloatintfloat

评论

0赞 user17732522 12/18/2022
"你不太可能得到正确的结果“:无论 OP 从哪里获得代码,都可能正是这种重新解释行为的意图。但是,要么是假定以特定方式编译的代码(例如使用标志),要么是特定于某些允许别名的编译器(我认为 MSVC 确实如此),要么是非常过时的依赖于编译器简单地按预期运行,而不管 UB 如何。-fno-strict-aliasing
0赞 john 12/18/2022
无论正确与否,代码的意图很可能是将构成的浮点位模式分配给实际的浮点变量。Tmp
0赞 Ted Lyngmo 12/18/2022
可能是。OP 说 a 做了正确的事情,但在这种情况下不会。static_cast*(float*)(&Tmp)
0赞 Aryan 12/18/2022 #2

存储整数

整数存储在其二进制(以 2 为基数)表示形式中。 占用 32 位。int32_t

存储浮点数

在大多数 C++ 编译器中,使用称为 IEEE 754 的标准来存储浮点数和双精度值。这与整数的存储方式有很大不同。

您可以使用某种 IEEE-754 浮点转换器自行检查数字的存储方式。

让我们考虑一个数字 5 的简单例子。

const uint Tmp = 5;

Tmp将像存储在内存中一样存储,因此将指向相同。0x00000005&Tmp

float ToFloat = *(float*)(&Tmp);

将其转换为浮点指针将被视为 IEEE 754 格式,具有值。0x000000057.00649232162e-45

static_cast

静态强制转换使用安全的隐式转换在兼容类型之间执行转换。

请注意,您正在将问题中的 from 转换为 ,这不是一个安全的转换。您可以安全地从 转换为 。int*float*intfloat

const uint32_t Tmp = 5;
float ToFloat = (float)Tmp; // 5

有关static_cast转换的更多信息。

评论

0赞 HolyBlackCat 12/19/2022
我建议提到这是UB。
0赞 user3840170 12/19/2022 #3

此代码尝试执行的操作是将浮点数的 32 位表示形式转换为实际的浮点值。IEEE 754 二进制浮点值(大多数情况下)是 (−1) 形式的数字的表示形式,× m × 2e。对于给定的浮点格式,机器字中一定数量的位被分配给 s(符号位)、m(尾数或有效位)和 e(指数位)中的每一个。这些位可以直接在位表示中操作,以构造任何可能的浮点值,绕过语言提供的普通算术运算的通常抽象。Aryan 的回答中提到的转换器是探索其工作原理的好方法。

不幸的是,这段代码不是符合标准的 C++(或者,实际上是 C),这要归功于一种叫做严格别名的东西。简而言之:如果一个变量被声明为整数类型,则只能通过指向同一整数类型的指针来访问它。不允许通过指向其他类型的指针访问它。

或多或少得到官方认可的版本是:

inline static float float_from_bits(std::uint32_t x) {
    float result;
    static_assert(sizeof(result) == sizeof(x),
        "float must be 32 bits wide");
    std::memcpy(&result, &x, sizeof(result));
    return result;
}

或者,在 C++20 中,简单地 .这仍然不能完全保证按照标准返回预期结果,但至少它不违反该特定规则。std::bit_cast<float>(x)

直接会将整数转换为浮点值,表示(近似)相同的数量。这不是直接的位转换:执行它需要计算 sme 的适当值,并根据数字格式将它们放在适当的位置。static_cast