是什么导致了浮点到无符号转换的差异?

Xev*_*ous 0 c++ floating-point floating-point-conversion

如果我运行这段代码:

#include <iostream>
#include <cstdint>

int main()
{
    double a = -2.0;
    const double b = -2.0;
    using std::cout;

    cout << "Direct cast double -> uint16:\n";
    cout << "a1: " << static_cast<std::uint16_t>(a) << "\n";
    cout << "b1: " << static_cast<std::uint16_t>(b) << "\n";
    auto a2 = static_cast<std::uint16_t>(a);
    auto b2 = static_cast<std::uint16_t>(b);
    cout << "a2: " << a2 << "\n";
    cout << "b2: " << b2 << "\n";

    cout << "Indirect cast double -> uint16:\n";
    cout << "a3: " << static_cast<std::uint16_t>(static_cast<std::int16_t>(a)) << "\n";
    cout << "b3: " << static_cast<std::uint16_t>(static_cast<std::int16_t>(b)) << "\n";
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我得到以下结果:

海湾合作委员会x86-64:

Direct cast double -> uint16:
a1: 0
b1: 0
a2: 0
b2: 0
Indirect cast double -> uint16:
a3: 65534
b3: 65534
Run Code Online (Sandbox Code Playgroud)

铿锵 x86-64:

Direct cast double -> uint16:
a1: 540684641
b1: 540684642
a2: 540684897
b2: 540684898
Indirect cast double -> uint16:
a3: 65534
b3: 65534
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  • 这里哪些转换是实现定义的?我知道浮点数的精度有限,但我绝不会期望a1b1a2b2是 4 个不同的值。对我来说这看起来像是一个编译器错误,特别是考虑到以下事实2^16 = 65536.
  • 该程序的不同编译器和平台之间允许的最大差异是多少?该代码源自生产代码,我们发现当代码在不同平台上运行时,值存在显着差异。
  • 为什么插入额外的(中间)演员会改变结果?a3在and的情况下,中间类型的引入是否会改变语义(代码的含义)b3

https://godbolt.org/z/K4njaKT7c

cht*_*htz 5

根据标准(https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf,第4.9节,\xc2\xa71),你会导致未定义的行为:

\n
\n

浮点类型的纯右值可以转换为整数类型的纯右值。转换截断;也就是说,小数部分被丢弃。如果截断值无法在目标类型中表示,则行为未定义。\n

\n
\n

UB 意味着任何事情都可能发生,程序甚至可以输出无法用 UB 表示的值uint16_t(例如540684641)。

\n