Any way faster than pow() to compute an integer power of 10 in C++?

后端 未结 12 707
逝去的感伤
逝去的感伤 2020-12-05 13:25

I know power of 2 can be implemented using << operator. What about power of 10? Like 10^5? Is there any way faster than pow(10,5) in C++? It is a pretty straight-forw

相关标签:
12条回答
  • 2020-12-05 13:38

    No multiplication and no table version:

    //Nx10^n
    int Npow10(int N, int n){
      N <<= n;
      while(n--) N += N << 2;
      return N;
    }
    
    0 讨论(0)
  • 2020-12-05 13:40

    if you want to calculate, e.g.,10^5, then you can:

    int main() {
       cout << (int)1e5 << endl; // will print 100000
       cout << (int)1e3 << endl; // will print 1000
       return 0;
    } 
    
    0 讨论(0)
  • 2020-12-05 13:44

    Based on Mats Petersson approach, but compile time generation of cache.

    #include <iostream>
    #include <limits>
    #include <array>
    
    // digits
    
    template <typename T>
    constexpr T digits(T number) {    
      return number == 0 ? 0 
                         : 1 + digits<T>(number / 10);
    }
    
    // pow
    
    // https://stackoverflow.com/questions/24656212/why-does-gcc-complain-error-type-intt-of-template-argument-0-depends-on-a
    // unfortunatly we can't write `template <typename T, T N>` because of partial specialization `PowerOfTen<T, 1>`
    
    template <typename T, uintmax_t N>
    struct PowerOfTen {
      enum { value = 10 * PowerOfTen<T, N - 1>::value };
    };
    
    template <typename T>
    struct PowerOfTen<T, 1> {
      enum { value = 1 };
    };
    
    // sequence
    
    template<typename T, T...>
    struct pow10_sequence { };
    
    template<typename T, T From, T N, T... Is>
    struct make_pow10_sequence_from 
    : make_pow10_sequence_from<T, From, N - 1, N - 1, Is...> { 
      //  
    };
    
    template<typename T, T From, T... Is>
    struct make_pow10_sequence_from<T, From, From, Is...> 
    : pow10_sequence<T, Is...> { 
      //
    };
    
    // base10list
    
    template <typename T, T N, T... Is>
    constexpr std::array<T, N> base10list(pow10_sequence<T, Is...>) {
      return {{ PowerOfTen<T, Is>::value... }};
    }
    
    template <typename T, T N>
    constexpr std::array<T, N> base10list() {    
      return base10list<T, N>(make_pow10_sequence_from<T, 1, N+1>());
    }
    
    template <typename T>
    constexpr std::array<T, digits(std::numeric_limits<T>::max())> base10list() {    
      return base10list<T, digits(std::numeric_limits<T>::max())>();    
    };
    
    // main pow function
    
    template <typename T>
    static T template_quick_pow10(T n) {
    
      static auto values = base10list<T>();
      return values[n]; 
    }
    
    // client code
    
    int main(int argc, char **argv) {
    
      long long sum = 0;
      int n = strtol(argv[1], 0, 0);
      const long outer_loops = 1000000000;
    
      if (argv[2][0] == 't') {
    
        for(long i = 0; i < outer_loops / n; i++) {
    
          for(int j = 1; j < n+1; j++) {
    
            sum += template_quick_pow10(n);
          }
        }
      }
    
      std::cout << "sum=" << sum << std::endl;
      return 0;
    }
    

    Code does not contain quick_pow10, integer_pow, opt_int_pow for better readability, but tests done with them in the code.

    Compiled with gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5), using -Wall -O2 -std=c++0x, gives the following results:

    $ g++ -Wall -O2 -std=c++0x main.cpp
    
    $ time ./a.out  8 a
    sum=100000000000000000
    
    real  0m0.438s
    user  0m0.432s
    sys 0m0.008s
    
    $ time ./a.out  8 b
    sum=100000000000000000
    
    real  0m8.783s
    user  0m8.777s
    sys 0m0.004s
    
    $ time ./a.out  8 c
    sum=100000000000000000
    
    real  0m6.708s
    user  0m6.700s
    sys 0m0.004s
    
    $ time ./a.out  8 t
    sum=100000000000000000
    
    real  0m0.439s
    user  0m0.436s
    sys 0m0.000s
    
    0 讨论(0)
  • 2020-12-05 13:46

    Something like this:

    int quick_pow10(int n)
    {
        static int pow10[10] = {
            1, 10, 100, 1000, 10000, 
            100000, 1000000, 10000000, 100000000, 1000000000
        };
    
        return pow10[n]; 
    }
    

    Obviously, can do the same thing for long long.

    This should be several times faster than any competing method. However, it is quite limited if you have lots of bases (although the number of values goes down quite dramatically with larger bases), so if there isn't a huge number of combinations, it's still doable.

    As a comparison:

    #include <iostream>
    #include <cstdlib>
    #include <cmath>
    
    static int quick_pow10(int n)
    {
        static int pow10[10] = {
            1, 10, 100, 1000, 10000, 
            100000, 1000000, 10000000, 100000000, 1000000000
        };
    
        return pow10[n]; 
    }
    
    static int integer_pow(int x, int n)
    {
        int r = 1;
        while (n--)
           r *= x;
    
        return r; 
    }
    
    static int opt_int_pow(int n)
    {
        int r = 1;
        const int x = 10;
        while (n)
        {
            if (n & 1) 
            {
               r *= x;
               n--;
            }
            else
            {
                r *= x * x;
                n -= 2;
            }
        }
    
        return r; 
    }
    
    
    int main(int argc, char **argv)
    {
        long long sum = 0;
        int n = strtol(argv[1], 0, 0);
        const long outer_loops = 1000000000;
    
        if (argv[2][0] == 'a')
        {
            for(long i = 0; i < outer_loops / n; i++)
            {
                for(int j = 1; j < n+1; j++)
                {
                    sum += quick_pow10(n);
                }
            }
        }
        if (argv[2][0] == 'b')
        {
            for(long i = 0; i < outer_loops / n; i++)
            {
                for(int j = 1; j < n+1; j++)
                {
                    sum += integer_pow(10,n);
                }
            }
        }
    
        if (argv[2][0] == 'c')
        {
            for(long i = 0; i < outer_loops / n; i++)
            {
                for(int j = 1; j < n+1; j++)
                {
                    sum += opt_int_pow(n);
                }
            }
        }
    
        std::cout << "sum=" << sum << std::endl;
        return 0;
    }
    

    Compiled with g++ 4.6.3, using -Wall -O2 -std=c++0x, gives the following results:

    $ g++ -Wall -O2 -std=c++0x pow.cpp
    $ time ./a.out 8 a
    sum=100000000000000000
    
    real    0m0.124s
    user    0m0.119s
    sys 0m0.004s
    $ time ./a.out 8 b
    sum=100000000000000000
    
    real    0m7.502s
    user    0m7.482s
    sys 0m0.003s
    
    $ time ./a.out 8 c
    sum=100000000000000000
    
    real    0m6.098s
    user    0m6.077s
    sys 0m0.002s
    

    (I did have an option for using pow as well, but it took 1m22.56s when I first tried it, so I removed it when I decided to have optimised loop variant)

    0 讨论(0)
  • 2020-12-05 13:47

    This function will calculate x ^ y much faster then pow. In case of integer values.

    int pot(int x, int y){
    int solution = 1;
    while(y){
        if(y&1)
            solution*= x;
        x *= x;
        y >>= 1;
    }
    return solution;
    

    }

    0 讨论(0)
  • 2020-12-05 13:51

    result *= 10 can also be written as result = (result << 3) + (result << 1)

    constexpr int pow10(int n) {
      int result = 1;
      for (int i = 0; i < n; i++) {
        result = (result << 3) + (result << 1);
      }
      return result;
    }
    
    0 讨论(0)
提交回复
热议问题