编译时可以平方而不溢出的最大值是多少整数类型?

问题描述

在 C++ 中,是否有一种编译时方法来计算可以安全平方的整数类型 T 的最大值,即数学上的 x * x <= std::numeric_limits<T>::max() 以便如果操作 {{1}被执行,它不会导致未定义的行为(对于有符号类型)或溢出(对于无符号类型?

我可以只限制在 x*x 中有特化的整数类型,即无需担心新的用户定义的整数类型,如果这样更容易的话。

当然,在数学上,答案是std::numeric_limits。 例如,对于 64 位有符号 long long,这将是 floor(sqrt(std::numeric_limits<T>::max()))。 A(即使对于大数也是正确的) constexpr sqrt 可以解决问题,尽管我不知道这是否是最好的方法。

答案应该是:

  • 64 位无符号:floor(sqrt(2^63-1)) == 3037000499
  • 64 位签名:floor(sqrt(2^64-1)) == 4294967295
  • 32 位无符号:floor(sqrt(2^63-1)) == 3037000499
  • 32 位签名:floor(sqrt(2^32-1)) == 65535

解决方法

  • 设置位的下半部分。
  • 对于有符号类型,加 1,然后除以 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;    
    }
}

Demo

或者只是手动定义值,因为尺寸数量非常有限:

#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; }

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...