I have implemented exponentiation with integer base and non-negative integer exponent for practicing purposes.
Of course there is some upper limit what numbers can be exponentiated within the int datatype, but I wasn't concerned about this at this point. But any suggestions how to handle it are welcome.
There are four variantions of the power function:
// full runtime version
int ipow(int, unsigned int);
// full compile time version
template<int, unsigned int> int ipow();
// only base is known at compile time
template<int> int ipow_base(unsigned int);
// only exponent is known at compile time
template<unsigned int> int ipow_exp(int);
I am interested in a general review, if I'm doing something wrong, could I have made something more explicit, etc...
My only restriction is that I am not (yet) intersted in post C++17 features.
#include <cstddef>
#include <stdexcept>
template<const unsigned int exponent>
constexpr int ipow_exp(int base)
{
if (exponent == 0) return 1;
return base * ipow_exp<exponent-1>(base);
}
template<>
constexpr int ipow_exp<1>(int base)
{
return base;
}
template<>
constexpr int ipow_exp<0>(int base)
{
if (base == 0) throw std::logic_error("0^0 is undefined.");
return 1;
}
template<const int base>
constexpr int ipow_base(unsigned int exponent)
{
if (exponent == 0) return 1;
return base * ipow_base<base>(exponent-1);
}
template<>
constexpr int ipow_base<2>(unsigned int exponent)
{
return 1 << exponent;
}
template<>
constexpr int ipow_base<1>(unsigned int exponent)
{
return 1;
}
template<>
constexpr int ipow_base<0>(unsigned int exponent)
{
if (exponent == 0) throw std::logic_error("0^0 is undefined.");
return 0;
}
template<const int base, const unsigned int exponent>
constexpr int ipow()
{
static_assert(exponent != 0 || base != 0, "0^0 is undefined.");
if (exponent == 1 || base == 0 || base == 1) {
return base;
}
return ipow_base<base>(exponent);
}
int ipow(int base, unsigned int exponent)
{
if (exponent == 0) {
if (base == 0) throw std::logic_error("0^0 is undefined.");
return 1;
}
int result = base;
while (--exponent > 0) {
result *= base;
}
return result;
}
#include <cassert>
int main()
{
int tmp;
bool thrown = false;
// full runtime version
assert(ipow(0,1) == 0);
assert(ipow(0,2) == 0);
assert(ipow(0,3) == 0);
assert(ipow(0,50) == 0);
assert(ipow(1,0) == 1);
assert(ipow(1,1) == 1);
assert(ipow(1,2) == 1);
assert(ipow(1,50) == 1);
assert(ipow(2,0) == 1);
assert(ipow(2,1) == 2);
assert(ipow(2,2) == 4);
assert(ipow(2,3) == 8);
assert(ipow(2,10) == 1024);
assert(ipow(3,0) == 1);
assert(ipow(3,1) == 3);
assert(ipow(3,2) == 9);
assert(ipow(3,3) == 27);
assert(ipow(3,4) == 81);
assert(ipow(5,0) == 1);
assert(ipow(5,1) == 5);
assert(ipow(5,2) == 25);
assert(ipow(5,3) == 125);
assert(ipow(5,4) == 625);
assert(ipow(-1,0) == 1);
assert(ipow(-1,1) == -1);
assert(ipow(-1,2) == 1);
assert(ipow(-1,3) == -1);
assert(ipow(-1,4) == 1);
assert(ipow(-1,31) == -1);
assert(ipow(-1,32) == 1);
assert(ipow(-2,0) == 1);
assert(ipow(-2,1) == -2);
assert(ipow(-2,2) == 4);
assert(ipow(-2,9) == -512);
assert(ipow(-2,10) == 1024);
assert(ipow(-5,0) == 1);
assert(ipow(-5,1) == -5);
assert(ipow(-5,2) == 25);
assert(ipow(-5,3) == -125);
assert(ipow(-5,4) == 625);
thrown = false;
try {
ipow(0,0);
} catch (std::logic_error e){
thrown = true;
}
assert(thrown);
// compile time exponent version
tmp = ipow_exp<1>(0); assert(tmp == 0);
tmp = ipow_exp<2>(0); assert(tmp == 0);
tmp = ipow_exp<3>(0); assert(tmp == 0);
tmp = ipow_exp<50>(0); assert(tmp == 0);
tmp = ipow_exp<0>(1); assert(tmp == 1);
tmp = ipow_exp<1>(1); assert(tmp == 1);
tmp = ipow_exp<2>(1); assert(tmp == 1);
tmp = ipow_exp<50>(1); assert(tmp == 1);
tmp = ipow_exp<0>(2); assert(tmp == 1);
tmp = ipow_exp<1>(2); assert(tmp == 2);
tmp = ipow_exp<2>(2); assert(tmp == 4);
tmp = ipow_exp<3>(2); assert(tmp == 8);
tmp = ipow_exp<10>(2); assert(tmp == 1024);
tmp = ipow_exp<0>(3); assert(tmp == 1);
tmp = ipow_exp<1>(3); assert(tmp == 3);
tmp = ipow_exp<2>(3); assert(tmp == 9);
tmp = ipow_exp<3>(3); assert(tmp == 27);
tmp = ipow_exp<4>(3); assert(tmp == 81);
tmp = ipow_exp<0>(5); assert(tmp == 1);
tmp = ipow_exp<1>(5); assert(tmp == 5);
tmp = ipow_exp<2>(5); assert(tmp == 25);
tmp = ipow_exp<3>(5); assert(tmp == 125);
tmp = ipow_exp<4>(5); assert(tmp == 625);
tmp = ipow_exp<0>(-1); assert(tmp == 1);
tmp = ipow_exp<1>(-1); assert(tmp == -1);
tmp = ipow_exp<2>(-1); assert(tmp == 1);
tmp = ipow_exp<3>(-1); assert(tmp == -1);
tmp = ipow_exp<4>(-1); assert(tmp == 1);
tmp = ipow_exp<31>(-1); assert(tmp == -1);
tmp = ipow_exp<32>(-1); assert(tmp == 1);
tmp = ipow_exp<0>(-2); assert(tmp == 1);
tmp = ipow_exp<1>(-2); assert(tmp == -2);
tmp = ipow_exp<2>(-2); assert(tmp == 4);
tmp = ipow_exp<9>(-2); assert(tmp == -512);
tmp = ipow_exp<10>(-2); assert(tmp == 1024);
tmp = ipow_exp<0>(-5); assert(tmp == 1);
tmp = ipow_exp<1>(-5); assert(tmp == -5);
tmp = ipow_exp<2>(-5); assert(tmp == 25);
tmp = ipow_exp<3>(-5); assert(tmp == -125);
tmp = ipow_exp<4>(-5); assert(tmp == 625);
thrown = false;
try {
ipow_exp<0>(0);
} catch (std::logic_error e){
thrown = true;
}
assert(thrown);
// compile time base version
tmp = ipow_base<0>(1); assert(tmp == 0);
tmp = ipow_base<0>(2); assert(tmp == 0);
tmp = ipow_base<0>(3); assert(tmp == 0);
tmp = ipow_base<0>(50); assert(tmp == 0);
tmp = ipow_base<1>(0); assert(tmp == 1);
tmp = ipow_base<1>(1); assert(tmp == 1);
tmp = ipow_base<1>(2); assert(tmp == 1);
tmp = ipow_base<1>(50); assert(tmp == 1);
tmp = ipow_base<2>(0); assert(tmp == 1);
tmp = ipow_base<2>(1); assert(tmp == 2);
tmp = ipow_base<2>(2); assert(tmp == 4);
tmp = ipow_base<2>(3); assert(tmp == 8);
tmp = ipow_base<2>(10); assert(tmp == 1024);
tmp = ipow_base<3>(0); assert(tmp == 1);
tmp = ipow_base<3>(1); assert(tmp == 3);
tmp = ipow_base<3>(2); assert(tmp == 9);
tmp = ipow_base<3>(3); assert(tmp == 27);
tmp = ipow_base<3>(4); assert(tmp == 81);
tmp = ipow_base<5>(0); assert(tmp == 1);
tmp = ipow_base<5>(1); assert(tmp == 5);
tmp = ipow_base<5>(2); assert(tmp == 25);
tmp = ipow_base<5>(3); assert(tmp == 125);
tmp = ipow_base<5>(4); assert(tmp == 625);
tmp = ipow_base<-1>(0); assert(tmp == 1);
tmp = ipow_base<-1>(1); assert(tmp == -1);
tmp = ipow_base<-1>(2); assert(tmp == 1);
tmp = ipow_base<-1>(3); assert(tmp == -1);
tmp = ipow_base<-1>(4); assert(tmp == 1);
tmp = ipow_base<-1>(31); assert(tmp == -1);
tmp = ipow_base<-1>(32); assert(tmp == 1);
tmp = ipow_base<-2>(0); assert(tmp == 1);
tmp = ipow_base<-2>(1); assert(tmp == -2);
tmp = ipow_base<-2>(2); assert(tmp == 4);
tmp = ipow_base<-2>(9); assert(tmp == -512);
tmp = ipow_base<-2>(10); assert(tmp == 1024);
tmp = ipow_base<-5>(0); assert(tmp == 1);
tmp = ipow_base<-5>(1); assert(tmp == -5);
tmp = ipow_base<-5>(2); assert(tmp == 25);
tmp = ipow_base<-5>(3); assert(tmp == -125);
tmp = ipow_base<-5>(4); assert(tmp == 625);
thrown = false;
try {
ipow_base<0>(0);
} catch (std::logic_error e){
thrown = true;
}
assert(thrown);
// full compile time version
tmp = ipow<0,1>(); assert(tmp == 0);
tmp = ipow<0,2>(); assert(tmp == 0);
tmp = ipow<0,3>(); assert(tmp == 0);
tmp = ipow<0,50>(); assert(tmp == 0);
tmp = ipow<1,0>(); assert(tmp == 1);
tmp = ipow<1,1>(); assert(tmp == 1);
tmp = ipow<1,2>(); assert(tmp == 1);
tmp = ipow<1,50>(); assert(tmp == 1);
tmp = ipow<2,0>(); assert(tmp == 1);
tmp = ipow<2,1>(); assert(tmp == 2);
tmp = ipow<2,2>(); assert(tmp == 4);
tmp = ipow<2,3>(); assert(tmp == 8);
tmp = ipow<2,10>(); assert(tmp == 1024);
tmp = ipow<3,0>(); assert(tmp == 1);
tmp = ipow<3,1>(); assert(tmp == 3);
tmp = ipow<3,2>(); assert(tmp == 9);
tmp = ipow<3,3>(); assert(tmp == 27);
tmp = ipow<3,4>(); assert(tmp == 81);
tmp = ipow<5,0>(); assert(tmp == 1);
tmp = ipow<5,1>(); assert(tmp == 5);
tmp = ipow<5,2>(); assert(tmp == 25);
tmp = ipow<5,3>(); assert(tmp == 125);
tmp = ipow<5,4>(); assert(tmp == 625);
tmp = ipow<-1,0>(); assert(tmp == 1);
tmp = ipow<-1,1>(); assert(tmp == -1);
tmp = ipow<-1,2>(); assert(tmp == 1);
tmp = ipow<-1,3>(); assert(tmp == -1);
tmp = ipow<-1,4>(); assert(tmp == 1);
tmp = ipow<-1,31>(); assert(tmp == -1);
tmp = ipow<-1,32>(); assert(tmp == 1);
tmp = ipow<-2,0>(); assert(tmp == 1);
tmp = ipow<-2,1>(); assert(tmp == -2);
tmp = ipow<-2,2>(); assert(tmp == 4);
tmp = ipow<-2,9>(); assert(tmp == -512);
tmp = ipow<-2,10>(); assert(tmp == 1024);
tmp = ipow<-5,0>(); assert(tmp == 1);
tmp = ipow<-5,1>(); assert(tmp == -5);
tmp = ipow<-5,2>(); assert(tmp == 25);
tmp = ipow<-5,3>(); assert(tmp == -125);
tmp = ipow<-5,4>(); assert(tmp == 625);
#ifdef TEST_COMPILE_ERRORS
ipow<0,0>();
#endif
return 0;
}
$ g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0