3

I use compile-time int powers calculation Power, to compute n**p. It uses ints. I want to calculate something that's bigger than int, ok ? It fits into u64 (unsigned long long). Can C++ templates do compile-time calculations on u64 ? Enums cannot do this. On doubles ? Possible ?

I would really want the type to be arg of the template. Is it possible ? My compiler is not c++0x.

Thanks Andrei

template<int N, int P> struct Power {
   enum { val = N * Power<N, P-1>::val };
};

template<int N> struct Power<N, 0> {
   enum { val = 1 };
};

int result = Power<2, 5>; // 2**5 == 32
Community
  • 1
  • 1
Andrei
  • 8,606
  • 10
  • 35
  • 43
  • For those that read in this year+ - C++11 and newer have support for constexpr, it's a lot simpler and more beautiful and does the same job. – Lilian A. Moraru Apr 21 '13 at 21:00

4 Answers4

3

Yes, you can do compile-time computations on any primitive integral type. You cannot, however, do computations on floating-point values because templates can't be parameterized over those values. The upcoming C++0x standard will introduce special classes to do compile-time rational arithmetic, so you may be able to use that if you wish.

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
  • You are saying that in pre-c0x c++, this is not possible, right. – Andrei Jan 05 '11 at 20:35
  • It's possible to use integral types in the current C++ standard. If you wanted to invest the energy to do so, you could build up the compile-time rational arithmetic libraries such that they would work with current C++ - they don't use any special language features - but the work will already be done for you in C++0x. And neither C++0x nor current C++ supports templates with real-valued arguments. – templatetypedef Jan 05 '11 at 20:37
2
template<int N, unsigned int P> struct Power {
   static const unsigned long long val = N * Power<N, P-1>::val;
};

template<int N> struct Power<N, 0> {
   static const unsigned long long val = 1;
}

Note that I made P an unsigned int, because your template would fail for negative values.

sbi
  • 219,715
  • 46
  • 258
  • 445
  • That doesn't compile pre-c0x. – Andrei Jan 05 '11 at 20:34
  • @Andrei: Why not? Both VC9 and [Comeau Online](http://www.comeaucomputing.com/tryitout/) accept it. – sbi Jan 05 '11 at 20:45
  • @Andrei: You've used "pre-c0x" twice now, so I don't think it's a typo. The next (and 3rd) edition of C++ is called C++0x, so you should use "pre-C++0x" to refer to previous editions. C0x might also be confused with [C1X](http://en.wikipedia.org/wiki/C1X), which is the next edition of the C standard and which I've previously seen called C0X. – Fred Nurk Jan 06 '11 at 13:05
  • @Fred: Well, at least this would render Andrei's comment true. The code will not compiled with a current C compiler. :) – sbi Jan 06 '11 at 13:08
  • @sbi: True, I didn't consider he might actually be talking about C. :) – Fred Nurk Jan 06 '11 at 13:18
  • Anatolyg's answer is the only portable solution so far, since C++03 doesn't have "long long" or any other built-in type guaranteed to be able to store any 64-bit integer. Most compilers provide such a type as an extension. – decltype Mar 01 '11 at 14:28
2

To expand on sbi's implementation, here's one that does exponentiation-by-squaring (which requires fewer template instantiations for large powers).

Note that if you really only want to calculate powers of 2, you're much better off just left-shifting (i.e., 2**x == 1 << x) rather than doing all this template stuff.

#include <iostream>

template <unsigned long long N, unsigned int P, int Odd = (P&1)> struct Power;

template <unsigned long long N, unsigned int P>
struct Power<N,P,0> { // even (square N and halve the power)
    static const unsigned long long val = Power<N*N,(P/2)>::val;
};

template <unsigned long long N, unsigned int P>
struct Power<N,P,1> { // odd (multiply by N and decrement the power)
    static const unsigned long long val = N * Power<N,(P-1)>::val;
};

template <unsigned long long N>
struct Power<N,0,0> { // zero (x**0 is 1 for all x != 0)
    static const unsigned long long val = 1;
};

int main() {
    std::cout << "2**0 = " << Power<2,0>::val << "\n";
    std::cout << "2**1 = " << Power<2,1>::val << "\n";
    std::cout << "2**2 = " << Power<2,2>::val << "\n";
    std::cout << "2**3 = " << Power<2,3>::val << "\n";
    std::cout << "2**4 = " << Power<2,4>::val << "\n";
    std::cout << "2**5 = " << Power<2,5>::val << "\n";
    std::cout << "2**6 = " << Power<2,6>::val << "\n";
    std::cout << "2**7 = " << Power<2,7>::val << "\n";
    std::cout << "2**8 = " << Power<2,8>::val << "\n";
    std::cout << "2**9 = " << Power<2,9>::val << "\n";
    std::cout << "2**10 = " << Power<2,10>::val << "\n";
    std::cout << "2**11 = " << Power<2,11>::val << "\n";
    std::cout << "2**12 = " << Power<2,12>::val << "\n";
    std::cout << "2**30 = " << Power<2,30>::val << "\n";
    std::cout << "2**40 = " << Power<2,40>::val << "\n";
    std::cout << "2**50 = " << Power<2,50>::val << "\n";
    std::cout << "2**60 = " << Power<2,60>::val << "\n";
    return 0;
}

Disclaimer: I make no claims that this code will necessarily compile faster due to the reduction in number of template instantiations (though it might). I really only wrote it as a toy demo. I leave it as an exercise for the reader to write a version that isn't vulnerable to someone explicitly passing a value for the Odd parameter when using Power<>.

John Bartholomew
  • 6,428
  • 1
  • 30
  • 39
  • `template struct Power { enum { Odd = P&1 }; static const unsigned long long val = (Odd?N:1)*Power::val; }` -- 33% less templates, half as much code, `Odd` bug removed. – Yakk - Adam Nevraumont Jan 04 '13 at 20:18
2

You can use multiple-precision mathematics to calculate large numbers (in my example below i use 96-bit calculations with 3 template parameters, you can use any constant number). You need to have more than one integer as template parameter.

When performing compile-time multiplication, you should probably multiply 32-bit numbers having 64-bit results; the result should be split into 2 template parameters.

Overflow checking is probably possible but may be tricky.

const uint64_t W = 1000000000; // word size: 2^32 is best; any smaller number is OK
// I use a power of 10 as word size for ease of printing (see main() below)

// The following class performs multiplication of (n0 + W*n1 + W*W*n2) by (base)
template <unsigned n0, unsigned n1, unsigned n2, uint64_t base, unsigned p> class power_temp
{
    typedef power_temp<
        n0 * base % W,
        n1 * base % W + n0 * base / W,
        n2 * base % W + n1 * base / W,
        base, p - 1> mult_type;
public:
    static const unsigned x0 = mult_type::x0;
    static const unsigned x1 = mult_type::x1;
    static const unsigned x2 = mult_type::x2;
};

// The following partial specialization is used to end recursion
template <unsigned n0, unsigned n1, unsigned n2, uint64_t base>
class power_temp<n0, n1, n2, base, 0>
{
public:
    static const unsigned x0 = n0;
    static const unsigned x1 = n1;
    static const unsigned x2 = n2;
};

// The following class calculates a power, using compile-time calculations
template <unsigned base, unsigned p> struct power
{
    static const unsigned x0 = power_temp<1, 0, 0, base, p>::x0;
    static const unsigned x1 = power_temp<1, 0, 0, base, p>::x1;
    static const unsigned x2 = power_temp<1, 0, 0, base, p>::x2;
};

int main()
{
    typedef power<123456789, 3> my1;
    printf("%09d%09d%09d\n", my1::x2, my1::x1, my1::x0);

    typedef power<5, 33> my2;
    printf("%09d%09d%09d\n", my2::x2, my2::x1, my2::x0);
}
anatolyg
  • 26,506
  • 9
  • 60
  • 134