如果我运行这段代码:
#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://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf,第 4.9 节,§1),您会导致未定义的行为:
浮点类型的纯右值可以转换为整数类型的纯右值。转换截断;也就是说,小数部分被丢弃。如果无法截断值,则行为未定义 以目的地类型表示。
UB 意味着任何事情都可能发生,程序甚至可以输出
uint16_t
无法表示的值(例如540684641)。