pow(2, 64) - 1
- это выражение double
, , а не int
, поскольку pow
не имеет перегрузки, которая возвращает целое числотип.Литерал 1 будет повышен до того же ранга, что и результат pow
Однако, поскольку двойная точность IEEE-754 имеет длину только 64 бита, вы никогда не сможете хранить значения, имеющие 64 значащих бита или более, например2 64 -1
Так что pow(2, 64) - 1
будет округлено до ближайшего представимого значения , которое само по себе pow(2, 64)
, и pow(2, 64) - 1 == pow(2, 64)
приведет к 1Наибольшее значение меньше 18446744073709549568 = 2 64 - 2048. Вы можете проверить это с помощью std::nextafter
На некоторых платформах (особенно x86, кромев MSVC) long double
имеет 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
На многих других платформах double
может иметь IEEE-754 с четверной точностью или дабл-дабл .Оба имеют более 64 битов значений, поэтому вы можете делать то же самое.Но, конечно, издержки будут выше
В любом случае, вы не должны использовать тип с плавающей точкой для целочисленной математики с самого начала.Мало того, что вычисление pow(2, x)
намного медленнее, чем 1ULL << x
, это также вызовет возникшую проблему из-за ограниченной точности double
.Вместо этого используйте uint64_t max2 = -1
или ((unsigned __int128)1ULL << 64) - 1
, если компилятор поддерживает этот тип