【发布时间】:2019-07-19 13:09:24
【问题描述】:
考虑以下几点:
#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 从字符串转换为uint64_t 的结果,按预期工作。
但是,如果我将结果转换为double,然后再转换为uint64_t,则它会打印第二个数字0。
通常,我会将此归因于浮点数的精度不准确,但更让我感到困惑的是,如果我将 ULLONG_MAX 从 uint64_t 转换为 double 然后返回到 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
【问题讨论】:
-
这是未定义行为的线索:在本地测试中,使用
g++6.3 版,行为会根据我是否通过优化标志而有所不同。当我通过-O1、-O2或-O3时,我符合你的行为。当我没有传递优化标志(或显式传递-O0)时,两次往返转换的结果都是0(检查程序集,只有-O0实际上执行z2的转换;优化会跳过它们根据eerorika's answer,标准的基础是任何情况下强制转换会产生影响的情况都是未定义的行为。 -
澄清检查程序集的结果(Radoslaw 的代码):
-O1及更高版本,z2实际上永远不存在;0xffffffffffffffff的立即值在打印之前直接加载到参数寄存器中,而不会存储在专用寄存器或堆栈位置中。只有在-O0(它避免了会干扰调试的优化;它试图保留代码行和相关程序集之间的对应关系)它才费心为z2创建一个堆栈位置,每次使用时都从它加载, 对值执行强制转换等。
标签: c++ precision strtol strtoull