这里造成浮点到无符号转换差异的原因是什么?

问题描述 投票:0回答:1

如果我运行这段代码:

#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;
}

我得到以下结果:

GCC x86-64:

Direct cast double -> uint16:
a1: 0
b1: 0
a2: 0
b2: 0
Indirect cast double -> uint16:
a3: 65534
b3: 65534

Clang x86-64:

Direct cast double -> uint16:
a1: 540684641
b1: 540684642
a2: 540684897
b2: 540684898
Indirect cast double -> uint16:
a3: 65534
b3: 65534

我的问题是:

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

https://godbolt.org/z/K4njaKT7c

c++ floating-point floating-point-conversion
1个回答
1
投票

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

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

UB 意味着任何事情都可能发生,程序甚至可以输出

uint16_t
无法表示的值(例如540684641)。

© www.soinside.com 2019 - 2024. All rights reserved.