有比pow()函数更快的方法在C++中计算10的整数次幂吗?

38

我知道使用 << 运算符可以实现2的幂。那么10的幂呢?例如10^5?在C++中是否有比pow(10,5)更快的方法?手动计算这是一个相当简单的过程。但是由于数字的二进制表示,对于计算机来说并不容易... 假设我只关心整数幂,即10^n,其中n为整数。


10
使用查找表? - user541686
1
查找表只有在你要求幂的整数值或可缩放为整数的小数值时才有用。如果它可以是任意浮点数,那么你只能使用 pow 或等效库。 - paddy
1
如果一侧始终是10,而另一侧是整数,您可以编写自己的表格并完成它。没有比这更快的了 - 这实际上只需要一个内存读取和一个或两个简单操作来索引到表中。 - Mats Petersson
@paddy。OP只是在谈论整数幂,否则<<就不会出现。 - Mad Physicist
13个回答

0

基于Mats Petersson的方法,但采用编译时生成缓存。

#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://dev59.com/2YHba4cB1Zd3GeqPQV0h
// 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;
}

为了更好的可读性,代码中不包含quick_pow10、integer_pow和opt_int_pow函数,但是在代码中进行了测试。

使用gcc版本4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)编译,使用-Wall -O2 -std=c++0x参数,得到以下结果:

$ 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

result *= 10 可以被写成 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;
}

这在任何常见的处理器/编译器组合上是否实际更快?它肯定不太易读。 - Konrad
请相信编译器处理这种优化的能力,它的表现比您想象的更好。 - Björn Sundin

0
如果你想计算10的5次方,可以这样做:
int main() {
   cout << (int)1e5 << endl; // will print 100000
   cout << (int)1e3 << endl; // will print 1000
   return 0;
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接