我有一个以下片段,我用它来尝试测试如果将最大整数转换为
double
:是否会发生精度损失
#include <cstdint>
#include <limits>
#include <iostream>
#include <iomanip>
int main () noexcept
{
uint64_t ui64{std::numeric_limits<uint64_t>::max()};
constexpr auto max_precision{std::numeric_limits<long double>::digits10 + 1};
std::cout << "ui64 " << std::setprecision(max_precision) << std::boolalpha << ui64 << "\n\n";
double f64 = static_cast<double>(ui64);
uint64_t ui64_cast_back = static_cast<uint64_t>(f64);
std::cout << "sizeof(f64): " << sizeof(double) << std::endl;
std::cout << "f64 = " << f64 << std::endl;
std::cout << "ui64_cast_back matches original value? " << (ui64_cast_back == ui64) << std::endl;
std::cout << "ui64_cast_back = " << ui64_cast_back << std::endl;
}
当我为项目的自定义平台构建它时(在编译器资源管理器上不可用),我得到以下输出:
ui64 18446744073709551615
sizeof(f64): 8
f64 = 18446744073709551616
ui64_cast_back matches original value? true
ui64_cast_back = 18446744073709551615
打印 double
值时出现一个错误似乎表明存在精度损失。然而,当回传时,会检索到原始值。 off 1 是否有可能是打印时 IO 流实现造成的,或者打印可以被认为是精度损失的证明?
double
看起来是binary64。 该格式中没有
18446744073709551615
。最接近的两个值是:
18446744073709549568 // The double below
18446744073709551615 // Your integer (not exactly representable)
18446744073709551616 // The double above
因此 double f64 = static_cast<double>(ui64);
将值四舍五入到最接近的双精度(上面的那个)。没有办法防止“精度损失”,因为它在物理上无法表示。 (考虑有 2^64 个
int64_t
值,最多 2^64 个 double 值)。您应该注意到,对于
std::numeric_limits<uint64_t>::max()-1
或
std::numeric_limits<uint64_t>::max()-2
等,您会得到相同的结果,因为它们也舍入为相同的值。当您执行强制转换时,您会出现未定义的行为,因为 double 现在太大而无法放入 int64_t 中。如果双精度太大,您的机器似乎只是返回最大的 int64_t 。