In C++, is there a compile-time way to compute the largest value of an integral type T
that can be safely squared, i.e. that mathematically x * x <= std::numeric_limits<T>::max()
so that if the operation x*x
were to be performed, it would not cause undefined behavior (for signed types) or overflow (for unsigned types?
I'm fine restricting to only integral types that have specializations in std::numeric_limits
, i.e. no need to worry about new user-defined integral types, if that makes it any easier.
Mathematically, of course, the answer is floor(sqrt(std::numeric_limits<T>::max()))
.
For example, this would be floor(sqrt(2^63-1)) == 3037000499
for a 64-bit signed long long. A (correct even for large numbers) constexpr sqrt would do the trick, though I don't know if that is the best way.
Answers should be:
floor(sqrt(2^64-1)) == 4294967295
floor(sqrt(2^63-1)) == 3037000499
floor(sqrt(2^32-1)) == 65535
floor(sqrt(2^31-1)) == 46340
sqrt(2)
#include <climits>
#include <limits>
#include <type_traits>
template<class T>
constexpr T largest_squareable() {
static_assert(std::is_integral_v<T>, "Must be an integral type");
if constexpr (std::is_unsigned_v<T>) {
return std::numeric_limits<T>::max() >> sizeof(T) * CHAR_BIT / 2;
} else {
constexpr long double sqrt_2 = 1.41421356237309504880L;
return (largest_squareable<std::make_unsigned_t<T>>() + 1) / sqrt_2;
}
}
Or just define the values by hand since there are a very limited number of sizes:
#include <cstdint>
template<typename T>
constexpr T largest_squareable();
template<> constexpr int8_t largest_squareable<int8_t>() { return 11; }
template<> constexpr uint8_t largest_squareable<uint8_t>() { return 15; }
template<> constexpr int16_t largest_squareable<int16_t>() { return 181; }
template<> constexpr uint16_t largest_squareable<uint16_t>() { return 255; }
template<> constexpr int32_t largest_squareable<int32_t>() { return 46340L; }
template<> constexpr uint32_t largest_squareable<uint32_t>() { return 65535UL; }
template<> constexpr int64_t largest_squareable<int64_t>() { return 3037000499LL; }
template<> constexpr uint64_t largest_squareable<uint64_t>() { return 4294967295ULL; }