I need a version of pow
for integers. I have 2 problems that I need to solve with pow
:
numeric_limits::max()
Does C++ provide me some kind of inline solution here, or am I stuck writing my own function:
template <typename T>
enable_if_t<is_integral_v<T>, T> mypow(const T base, unsigned int exp) {
T result = exp == 0U ? base : 1;
while(exp-- > 1U) {
if(numeric_limits<T>::max() / result <= base) return numeric_limits<T>::max();
result *= base;
}
return result;
}
Does C++ provide me some kind of inline solution here
No, there is no integer pow
in the standard library.
or am I stuck writing my own function
Yes, you get to write your own function. Note that your shown multiplication loop may be slower than using std::pow
to implement the function, especially since you also have a branch and division in the loop:
template<class I>
I int_pow_no_overflow(I base, I exp)
{
double max = std::numeric_limits<I>::max();
double result = std::round(std::pow(base, exp));
return result >= max
? max
: result;
}
For a more generic approach, you may want to consider underflow as well.
There are as well other, faster algorithms (see for example Exponentiation by squaring) for integer exponentiation than the linear one you showed, but I'm unsure whether it would be worth considering them unless you deal with arbitrary precision arithmetic, or an embedded system with no floating point unit.
Your code did not compile, you should if possible check your code compiles first, use your compiler, or check it on compiler explorer first.
Also, you forgot to take into account negative values. That's a very important feature of integral powers. Code below is for regular int type. I'll let you explore how you could extend it for other integral types.
#include <type_traits>
#include <iostream>
#include <cmath>
#include <limits>
using namespace std;
template <typename T>
enable_if_t< is_integral<T>::value, T>
mypow(T base, unsigned int exp)
{
T result = T(1);
bool sign = (base < 0);
if (sign) base = -base;
T temp = result;
while(exp-- != 0)
{
temp *= base;
if (temp < result)
{
return (sign) ? numeric_limits<T>::min()
: numeric_limits<T>::max();
}
result = temp;
}
return (sign && (exp & 1)) ? -result : result;
}
template <typename T>
enable_if_t< !is_integral<T>::value, int>
mypow(const T& base, unsigned int exp)
{
T result = T(1);
int i_base = int(floor(base + .5));
bool sign = (i_base < 0);
if (sign) i_base = -i_base;
int temp = result;
while(exp-- != 0)
{
temp *= i_base;
if (temp < result)
{
return (sign) ? numeric_limits<int>::min() : numeric_limits<int>::max();
}
result = temp;
}
return (sign && (exp & 1)) ? -result : result;
}
In real life, I would do this note the use of floor, even in the integral case.
template<typename T>
enable_if_t< is_integral<T>::value, T>
mypow(T x, unsigned int y) { return T(floor(pow(x, y) + .5)); }
template<typename T>
enable_if_t< !is_integral<T>::value, int>
mypow(T x, unsigned int y) { return int(floor(pow(floor(x + .5), y) + .5)); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With