0

I am sort of new to the language features of meta programming and I am trying to make a simple class with public static const variables that will set its values by compile time constants:

What I'm trying to achieve: I want to compute values that are the power of some exponent that are measured in number of bytes converted to number of bits with a base of 2. All calculations are in base 2.

Examples:

 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

I've tried writing a function to do the calculations to compute the values needed while trying to use constexpr or const, and I've tried using templates. I would like to use the const function, constexpr function or function template as such:


// 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 );
};

Through my efforts I've generated all sorts of compile time, run time errors ect. The latest attempt I was able to get the template version of the pow2<>() above to compile and run, however I'm not getting the correct results.

I'm not sure if my implementation of pow2 is wrong or if my syntax is wrong, or if I'm not using const or constexpr correctly and in some cases I kept getting the integral constant overflow as a compile time error from MS Visual Studio 2017 CE compiler.

I've been following these patterns for the pow2() function:

I can not seem to wrap my mind around this and don't know what else to try.

2 Answers 2

1

Notice that your last case isn't possible currently. You can't store 2^64 in an 8 byte type, the maximum is 2^64 - 1. At least on mainstream architectures, don't know which one you are using.

I see two problems with your function template.

  1. You multiply the result with base only once, but you decrement the bits count by 8 by doing expInBytes - 1. So, you need to multiply it eight times:

    return (expInBits == 0 ? 1 : base * base * base * base * base * base * base * base * pow2<expInBytes-1>() );
    
  2. The specialization for 0 returns 0, and any number multiplied with 0 is 0. :) If you think that you handled the case with expInBits == 0, think again: The only way for expInBits to be 0 is if expInBytes is 0, but that cannot be in the primary template because you have a specialization for when expInBytes is 0! That means that that branch is never taken, it literally has no effect.

Your function has the same problem described in 1), and in addition you are passing the wrong value to it when recursing (expInBits instead of expInBytes) and the order is wrong (base comes last).

In my opinion, a loop is easier to understand and less error-prone:

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;
}
Sign up to request clarification or add additional context in comments.

7 Comments

Yeah as for the last one yes I kind of figured that was giving me "integral overflow" for the value and that's fine. As for the base coming last; some of the examples I was basing this off I was guessing that the base was x and that the exp was y as in x^y.
I'm just curious how to handle the last case to show a larger number; for example windows calculator does calculations higher than what a 64bit integer can hold....
@FrancisCugler You can always have types that have arbitrary precision, using an array for example, or strings, or whatever. There is no built in however for this.
Yeah I do know that much, I guess I could display the largest possible value which is 2^64-1 and then append the string +1 for any kind of display out put. The other alternative would be to have the value returned or displayed in e notation as in 9.876e12...
Actually it wouldn't be appending a +1 that would be inappropriate. I would be actually be 2^(64-1) displayed in a 64bit int and then append the string or char array *2.
|
0

With the help of Rakete111 and his positive feed back; I was able to work through my problem as he pointed out a few mistakes. In return I was able to achieve some resemblance of what I wanted. To compute 2^n at compile time.


Here is the working code:

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

Thank you so much for your help and pointing me in the right direction. I will accept your answer.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.