Home > Blockchain >  Define a macro which defines a pow function only in the case where exponent is an integer
Define a macro which defines a pow function only in the case where exponent is an integer

Time:10-22

Following to a profiling on my C code, it appears that pow function is used a lot.

Some of my pow functions have integer exponent and other non integer exponent. I am only interested for the ones with integer exponent.

To gain in performance, I am looking a way to define a macro like this kind :

#define pow(x,n) ({\
    double product;\
    if (typeid(n).name() == "i") {\
    for(int i = 0; i < n-1; i  )\
        product *= x;}\
    else\
    product = pow(x,n);\
    product;\
})

But I don't get the gain expected regarding the runtime. I think this is due to the else part in my macro where I call the classical pow function.

I don't know how to determine in advance the type of exponent before macro was "written" during the pre-processing.

Ideally, I would like this macro only to be applied if exponent is an integer, but it seems my attempt is not pertinent.

EDIT :

From your suggestions, I tried 3 options :

First option : Just add overload inline functions with base which is integer or double :

// First option
inline int pow(int x, int n){
    // using simple mechanism for repeated multiplication
    int product = 1;
    for(int i = 0; i < n; i  ){
        product *= x;
    }
    return product;
}

inline int pow(double x, int n){
    // using simple mechanism for repeated multiplication
    double product = 1;
    for(int i = 0; i < n; i  ){
        product *= x;
    }
    return product;
}

Result : runtime = 1m 08s

Second option : Define a macro that calls via inline my_pow function if exponent n is not integer :

// Second option
inline double my_pow(double x, double n){
    return pow(x,n);
}

#define pow(x,n) ({\
    double product = 1;\
    if (typeid(n) == typeid(int)) {\
    for(int i = 0; i < n; i  )\
        product *= x;}\
    else product = my_pow(x,n);\
    product;\
})

Result : runtime = 51.86s

Third option : suggestion given in answer with template<typename type_t>

template<typename type_t>
inline double pow(const double x, type_t n)
{
    // this is compile time checking of types
    // don't use the rtti thing you are trying to do
    //if constexpr (is_floating_point_v<type_t>)
    if (typeid(n) != typeid(n))
    {
        return pow(x, n);
    }
    else
    {
        double value = 1;
        for (type_t i = 0; i < n;   i) value *= x;
        return value;
    }
}

Result : runtime = 52.84s

So finally, from these first tests, the best option would be the second one where I use macro combined with a function that calls the general case of pow function (both integer and floating exponent).

Could anyone suggest a more efficient solution or basically give his opinion about this best second option ?

CodePudding user response:

If you only need to switch between floating point types or not you can use templates instead of macros.

#include <cassert>
#include <cmath>
#include <type_traits>

namespace my_math
{
    template<typename type_t>
    inline double pow(const double x, type_t n)
    {
        // this is compile time checking of types
        // don't use the rtti thing you are trying to do 
        if constexpr (std::is_floating_point_v<type_t>)
        {
            return std::pow(x, n);
        }
        else
        {
            double value = 1;
            for (type_t i = 0; i < n;   i) value *= x;
            return value;
        }
    };

}

int main()
{
    assert(my_math::pow(2, 0) == 1);
    assert(my_math::pow(2.0, 1) == 2.0);
    assert(my_math::pow(3.0, 2.0) == 9.0);
    assert(my_math::pow(4.0f, 3.0f) == 64.0f);

   return 0;
}

CodePudding user response:

Use the c standard https://www.cplusplus.com/reference/ctgmath/ for macro pow version. or use constexpr statement for a pow function instead of a macro.

CodePudding user response:

Once you are sure your pow() is used, hereby a proposal to make your pow() function even better.

This idea might be difficult to implement, but in case you are regularly taking high powers, it might be worth-wile, let me show you an example:

You want to calculate pow(a,17).
Using your system, you need 16 multiplications.

Now let's turn 17 into binary, you get 10001, which means that, after some calculations, you can write pow(a,17) as:

square(square(square(square(a))))*a, where square(a) = a*a

This leaves you with just 5 multiplications and might cause a performance increase. In fact, it goes from O(n) to O(log n) (where n is the exponent).

  • Related