是否可以保证浮点变量的副本将按位等效于原始副本?



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

C++标准或一般标准中的任何内容是否向我保证,浮点左值在复制到另一个浮点变量或用作 const-ref 或按值参数时,将始终按位等效于原始值?

是否有任何因素会导致复制的浮点数按位查询原始值,例如更改浮点环境或将其传递到不同的线程中?

下面是一些基于我用来检查测试用例中浮点值等效性的示例代码,这个代码会失败,因为它需要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,而是将其留给实现(强调我的(:

[基本基础/12]

有三种浮点类型floatdoublelong doubledouble型提供至少与float一样多的精度,而long double型提供至少与double一样多的精度。 float 类型的值集是类型 double 的值集的子集;double 类型的值集是 long double 类型的值集的子集。 浮点类型的值表示形式是实现定义的。[ 注:本文档对浮点运算的准确性没有要求;另见[support.limits]

你编写的C++代码是对你希望抽象机器做什么的高级抽象描述,它完全掌握在编译器手中。"赋值"是C++标准的一个方面,如上所示,C++标准并不强制要求浮点运算的行为。要验证语句"赋值保持浮点值不变">,您的编译器必须根据C++抽象机器指定其浮点行为,我还没有看到任何此类文档(尤其是对于 MSVC(。

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

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

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

相关内容

  • 没有找到相关文章

最新更新