是否保证 float 变量的副本与原始变量按位等效?

Is it guaranteed that the copy of a float variable will be bitwise equivalent to the original?

我正在研究浮点确定性并且已经研究了这么多令人惊讶的不确定性的潜在原因,我开始对复制浮点数产生偏执:

C++ 标准中的任何内容或一般保证浮点左值在被复制到另一个浮点变量后或用作常量引用或按值参数时,将始终按位等效于原始值值?

任何事情都会导致复制的浮点数与原始值按位不等价,例如更改浮点环境或将其传递到不同的线程吗?

这是一些示例代码,基于我用来检查测试用例中浮点值是否相等的代码,这个代码会失败,因为它期望 FE_TONEAREST:

#include <cfenv>
#include <cstdint>

// MSVC-specific pragmas for floating point control
#pragma float_control(precise, on)
#pragma float_control(except, on)
#pragma fenv_access(on)
#pragma fp_contract(off)

// May make a copy of the floats
bool compareFloats(float resultValue, float comparisonValue)
{
    // I was originally doing a bit-wise comparison here but I was made
    // aware in the comments that this might not actually be what I want
    // so I only check against the equality of the values here now
    // (NaN values etc. have to be handled extra)
    bool areEqual = (resultValue == comparisonValue);

    // Additional outputs if not equal
    // ...

    return areEqual;
}

int main()
{
    std::fesetround(FE_TOWARDZERO)
    float value = 1.f / 10;
    float expectedResult = 0x1.99999ap-4;

    compareFloats(value, expectedResult);
}

我是否必须担心,如果我将一个浮点值传递给比较函数,即使它是左值,它在另一端的结果可能会有所不同?

没有这样的保证。

次正规、非正规化浮点数和 NaN 都是位模式可能不同的情况。

我相信带符号的负零在赋值时可以变成带符号的正零,尽管 IEEE754 不允许这样做。

C++ 标准本身对浮点数学几乎没有任何保证,因为它不强制要求 IEEE-754,而是将其留给实现(强调我的):

[basic.fundamental/12]

There are three floating-point types: float, double, and long double. The type double provides at least as much precision as float, and the type long double provides at least as much precision as double. The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double. The value representation of floating-point types is implementation-defined. [ Note: This document imposes no requirements on the accuracy of floating-point operations; see also [support.limits]. — end note ]

您编写的 C++ 代码是对您希望抽象机执行的操作的高级抽象描述,它完全掌握在编译器的手中。 "Assignments" 是 C++ 标准的一个方面,如上所示,C++ 标准不强制浮点运算的行为。要验证语句 "assignments leave floating point values unchanged",您的 编译器 必须根据 C++ 抽象机指定其浮点行为,而我'我没有看到任何此类文档(尤其是 MSVC)。

换句话说:如果没有确定确切的编译器、编译器版本、编译标志等,就不可能确定 C++ 程序的浮点语义是什么(尤其是关于舍入等困难情况, NaN 或带符号的零)。大多数编译器区分严格的 IEEE 一致性和放宽其中一些限制,但即便如此,您也不一定能保证程序在非优化版本和优化版本中具有相同的输出,例如常量折叠、中间结果的精度等上。

以防万一:对于 gcc,即使使用 -O0,您的程序也不会在 运行 时计算 1.f / 10,而是在编译时和因此您的舍入模式设置将被忽略:https://godbolt.org/z/U8B6bc

您不应该特别偏执于复制浮点数,而应该偏执于一般的浮点编译器优化。