【问题标题】:c++ computing compile time constants while preventing integral constant overflowc ++计算编译时间常数,同时防止积分常数溢出
【发布时间】:2018-01-04 06:03:31
【问题描述】:

我对元编程的语言特性有点陌生,我正在尝试用public static const variables 制作一个简单的class,它将通过编译时间常量设置其值:

我想要实现的目标:我想计算一些指数的幂,这些指数以转换为基数为2 的位数为单位的字节数来衡量。所有计算均以 2 为基数。

例子:

 1 byte(s) =  8 bits: value = pow(2, 8)  = 256;
 2 byte(s) = 16 bits: value = pow(2, 16) = 65536
 4 byte(s) = 32 bits: value = pow(2, 32) = 4294967296
 8 byte(s) = 64 bits: value = pow(2, 64) = 18446744073709551616

我尝试编写一个函数来计算在尝试使用constexprconst 时所需的值,并且我尝试使用templates。我想像这样使用const functionconstexpr functionfunction template


// constexpr function
constexpr std::uint64_t pow2( const std::uint32_t expInBytes, const std::uint32_t base = 2 ) {
    const std::uint32_t expInBits = expInBytes * CHAR_BIT;
    return static_cast<std::uint64_t>( expInBits == 0 ? 1 : base * pow2( base, expInBits - 1 ) );
}


// or function template
template<std::uint32_t expInbytes>
constexpr std::uint64_t pow2() {
    const std::uint32_t base = 2;
    const std::uint32_t expInBits = expInBytes * CHAR_BIT;
    return (expInBits == 0 ? 1 : base * pow2<expInBytes-1>() );
}

template<>
constexpr std::uint64_t pow2<0>() {
    return 0;
};

// template parameter T not used but needed to use the class as such:
// BitCombinations<>::static_member;
template<typename T = const std::uint32_t>
class BitCombinations { 
public:                                    // template    // non template
    static const std::uint64_t  ONE_BYTE    = pow2<1>();  // pow2( 1 );
    static const std::uint64_t  TWO_BYTES   = pow2<2>();  // pow2( 2 );
    static const std::uint64_t  FOUR_BYTES  = pow2<4>();  // pow2( 4 );
    static const std::uint64_t  EIGHT_BYTES = pow2<8>();  // pow2( 8 );
};

通过我的努力,我产生了各种编译时、运行时错误等。最新尝试我能够获得上面pow2&lt;&gt;() 的模板版本来编译和运行,但是我没有得到正确的结果。

我不确定我对pow2 的实现是否错误,或者我的语法是否错误,或者我是否没有正确使用constconstexpr,在某些情况下我一直得到integral constant overflow作为 MS Visual Studio 2017 CE 编译器的编译时错误。

我一直在为 pow2() 函数遵循这些模式:

我似乎无法解决这个问题,也不知道还能尝试什么。

【问题讨论】:

    标签: c++ templates c++14 c++17 constexpr


    【解决方案1】:

    请注意,您的最后一个案例目前是不可能的。 8字节类型不能存储2^64,最大是2^64 - 1。至少在主流架构上,不知道你用的是哪一种。

    我发现你的函数模板有两个问题。

    1. 您只将结果与base 相乘一次,但您通过执行expInBytes - 1 将位数减8。因此,您需要将其乘以八次:

      return (expInBits == 0 ? 1 : base * base * base * base * base * base * base * base * pow2<expInBytes-1>() );
      
    2. 0 的特化返回 0,任何乘以 0 的数都是 0。:) 如果你认为你用 expInBits == 0 处理了这种情况,再想一想:expInBits 的唯一方法如果expInBytes 为0,则为0,但这不能在主模板中,因为您对expInBytes 为0 时有专门化!这意味着该分支永远不会被采用,它实际上没有任何效果。

    您的函数存在与 1) 中描述的相同问题,此外,您在递归时传递了错误的值(expInBits 而不是expInBytes)并且顺序错误(基数在最后)。

    在我看来,循环更容易理解且不易出错:

    constexpr std::uint64_t pow2(const std::uint32_t expInBytes, const std::uint32_t base = 2) {
        const std::uint32_t expInBits = expInBytes * CHAR_BIT;
    
        std::uint64_t result = 1;
        for (std::uint32_t i = 0; i < expInBits; ++i)
          result *= base;
        return result;
    }
    

    【讨论】:

    • 是的,至于最后一个,是的,我有点认为这给了我“积分溢出”的价值,这很好。至于最后来的基地;我以此为基础的一些例子我猜basex,而expy,就像x^y一样。
    • 我只是好奇如何处理最后一个案例以显示更大的数字;例如,windows calculator 的计算量比 64bit integer 所能承受的要高....
    • @FrancisCugler 您始终可以拥有具有任意精度的类型,例如使用数组、字符串或其他。但是没有内置的。
    • 是的,我知道很多,我想我可以显示最大可能的值,即 2^64-1,然后附加字符串 +1 用于任何类型的显示输出。另一种选择是以e 表示法返回或显示值,如9.876e12...
    • 实际上它不会附加一个不合适的+1。我实际上会将2^(64-1) 显示在一个 64 位 int 中,然后附加字符串或字符数组 *2
    【解决方案2】:

    在 Rakete111 和他的积极反馈的帮助下;当他指出一些错误时,我能够解决我的问题。作为回报,我能够实现我想要的一些相似之处。在编译时计算2^n


    这是工作代码:

    inline constexpr std::uint64_t powerOfBits( const std::uint64_t base, std::uint64_t const exponent ) {
        return (exponent == 0) ? 1 : (base * powerOfBits( base, exponent - 1 ));
    }
    
    /*template<typename T = const std::uint32_t>*/
    class BitCombinations { 
    public:
        // Because I don't care for "magic numbers"
        static const std::uint64_t binaryBase = std::uint64_t(2); 
        static const std::uint64_t eightBits     = std::uint64_t( 8 );
        static const std::uint64_t sixteenBits   = std::uint64_t( 16 );
        static const std::uint64_t thirtyTwoBits = std::uint64_t( 32 );
        static const std::uint64_t sixtyFourBits = std::uint64_t( 64 );
        // Now Generate Our Compile Time Constants
        static const std::uint64_t  ONE_BYTE    = powerOfBits( binaryBase , eightBits );  // 
        static const std::uint64_t  TWO_BYTES   = powerOfBits( binaryBase , sixteenBits );  // 
        static const std::uint64_t  FOUR_BYTES  = powerOfBits( binaryBase , thirtyTwoBits );  // 
        // For 64bit int need to subtract 1 from the exponent otherwise you will have integral overflow
        // To prevent this we just take 2^63, then in any output display we will have to append the
        // string or characters `x 2` so that the user knows the value is double what they are seeing.
        static const std::uint64_t  EIGHT_BYTES = powerOfBits( binaryBase , sixtyFourBits - 1 ); 
    };
    

    int main() {
        std::cout << BitCombinations::ONE_BYTE << std::endl;
        std::cout << BitCombinations::TWO_BYTES << std::endl;
        std::cout << BitCombinations::FOUR_BYTES << std::endl;
        // Remember that 2^64 causes overflow: need to append characters to user.
        std::cout << BitCombinations::EIGHT_BYTES << " x 2" << std::endl;
        std::cout << std::endl;
    
        std::cout << "\nPress any key and enter to quite." << std::endl;
        char q;
        std::cin >> q;
        return 0;
    }
    

    非常感谢您的帮助并为我指明了正确的方向。我会接受你的回答。

    【讨论】:

      猜你喜欢
      • 2017-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-19
      • 1970-01-01
      • 2013-11-25
      相关资源
      最近更新 更多