8

考虑以下:

#include <iostream>
#include <cstdint>

int main() {
   std::cout << std::hex
      << "0x" << std::strtoull("0xFFFFFFFFFFFFFFFF",0,16) << std::endl
      << "0x" << uint64_t(double(std::strtoull("0xFFFFFFFFFFFFFFFF",0,16))) << std::endl
      << "0x" << uint64_t(double(uint64_t(0xFFFFFFFFFFFFFFFF))) << std::endl;
   return 0;
}

哪个打印:

0xffffffffffffffff
0x0
0xffffffffffffffff

第一个数字只是ULLONG_MAX从字符串转换为 a的结果uint64_t,它按预期工作。

但是,如果我将结果转换为double,然后再转换为uint64_t,那么它会打印0第二个数字 。

通常,我会将其归因于浮点数的精度不准确,但更让我感到困惑的是,如果我将ULLONG_MAXfromuint64_t转换为double然后返回 to uint64_t,结果是正确的(第三个数字)。

为什么第二个和第三个结果之间存在差异?

编辑(@Radoslaw Cybulski)对于另一个在这里发生的事情,试试这个代码:

#include <iostream>
#include <cstdint>
using namespace std;

int main() {
    uint64_t z1 = std::strtoull("0xFFFFFFFFFFFFFFFF",0,16);
    uint64_t z2 = 0xFFFFFFFFFFFFFFFFull;
    std::cout << z1 << " " << uint64_t(double(z1)) << "\n";
    std::cout << z2 << " " << uint64_t(double(z2)) << "\n";
    return 0;
}

愉快地打印:

18446744073709551615 0
18446744073709551615 18446744073709551615
4

1 回答 1

10

最接近 0xFFFFFFFFFFFFFFFF 并且可以用 double 表示的数字(假设 64 位 IEEE)是 18446744073709551616。您会发现这是一个比 0xFFFFFFFFFFFFFFFF 更大的数字。因此,该数字超出了 的可表示范围uint64_t

关于转换回整数,标准说(引用最新草案):

[conv.fpint]

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


为什么第二个和第三个结果之间存在差异?

因为程序的行为是未定义的。

尽管分析 UB 差异的原因几乎没有意义,因为变化的范围是无限的,但我对这种情况下差异的原因的猜测是,在一种情况下,该值是编译时间常数,而在另一种情况下,存在调用在运行时调用的库函数。

于 2019-07-19T13:21:04.653 回答