为什么uint64_t无法正确显示pow(2,64)-1?

时间:2019-03-01 16:14:26

标签: c++ floating-point double uint64

我试图了解为什么uint64_t类型不能正确显示pow(2,64)-1。 cplusplus标准为199711L。

我在C ++ 98标准下检查了pow()函数,

double pow (double base     , double exponent);
float pow (float base      , float exponent);
long double pow (long double base, long double exponent);
double pow (double base     , int exponent);
long double pow (long double base, int exponent);

所以我写了以下代码段

double max1 = (pow(2, 64) - 1);
cout << max1 << endl;

uint64_t max2 = (pow(2, 64) - 1);
cout << max2 << endl;

uint64_t max3 = -1;
cout << max3 << endl;

输出为:

max1: 1.84467e+019
max2: 9223372036854775808
max3: 18446744073709551615

2 个答案:

答案 0 :(得分:5)

浮点数的精度有限。

在您的系统上(通常,假定为Binary64 IEEE-754格式)18446744073709551615不是以double格式表示的数字。确实具有代表性的最接近的数字恰好是18446744073709551616

将两个幅值截然不同的浮点数相减(并相加)通常会产生错误。相对于较小的操作数,此错误可能很严重。在18446744073709551616. - 1. -> 18446744073709551616.的情况下,减法误差为1,实际上与较小的操作数相同。

当浮点值转换为整数类型,并且该值不能适合整数类型时,程序的行为是不确定的-即使整数类型是无符号的。

答案 1 :(得分:0)

pow(2, 64) - 1double表达式 不是 int,和{{1} }没有任何返回整数类型的重载。文字1将被提升为与pow

结果相同的等级

但是,因为IEEE-754的双精度只有64位长,所以您永远不能存储具有64个有效位或更多的值,例如2 64 -1

因此,pow将四舍五入为最接近的可表示值,即pow(2, 64) - 1本身,而pow(2, 64)将得出1。较小的最大值比它是18446744073709549568 = 2 64 -2048。您可以使用std::nextafter

进行检查

在某些平台上(尤其是x86,在MSVC上除外), long double确实具有64 bits of significand ,因此在这种情况下,您将获得正确的值。 following snippet

pow(2, 64) - 1 == pow(2, 64)

打印出

double max1 = pow(2, 64) - 1;
std::cout << "pow(2, 64) - 1 = " << std::fixed << max1 << '\n';
std::cout << "Previous representable value: " << std::nextafter(max1, 0) << '\n';
std::cout << (pow(2, 64) - 1 == pow(2, 64)) << '\n';

long double max2 = pow(2.0L, 64) - 1.0L;
std::cout << std::fixed << max2 << '\n';

在许多其他平台上,pow(2, 64) - 1 = 18446744073709551616.000000 Previous representable value: 18446744073709549568.000000 1 18446744073709551615.000000 可以是IEEE-754 quadruple-precisiondouble-double。两者的有效位都超过64位,因此您可以执行相同的操作。但是当然开销会更高

无论如何,您不应该从一开始就将浮点类型用于整数数学。计算double的速度不仅比pow(2, x)慢得多,而且由于1ULL << x的精确度有限,还会引起您看到的问题。请改用double,如果编译器支持该类型,请使用uint64_t max2 = -1