快速整数到十进制转换

时间:2012-05-07 20:12:23

标签: c performance data-conversion

给定一个(无符号)整数,将它转换为包含十进制表示的字符串的最常用方法是什么?

这种天真的做法是重复除以10,直到你达到零。我不喜欢这种方法,因为它

  • 使用整数除法,这在某些集成平台上既慢又不可用
  • 要求程序员随后翻转字符串。这使所需的内存操作数量翻倍。

我想到了以下方法将整数转换为十进制基数。这是一个好主意吗?如何在printf等函数的常见实现中完成此操作?

#include <stdint.h>

const static uint64_t i64_tab[20] = {
                     1u,
                    10u,
                   100u,
                  1000u,
                 10000u,
                100000u, /* 10^ 5 */
               1000000u,
              10000000u,
             100000000u,
            1000000000u,
           10000000000u, /* 10^10 */
          100000000000u,
         1000000000000u,
        10000000000000u,
       100000000000000u,
      1000000000000000u, /* 10^15 */
     10000000000000000u,
    100000000000000000u,
   1000000000000000000u,
  10000000000000000000u  /* 10^19 */
};

void uint64_to_string(char *out, uint64_t in) {
  int i;
  uint64_t tenpow;
  char accum;

  for (i = 19;i > 0;i--) {
    if (in >= i64_tab[i]) break;
  }

  do {
    tenpow = i64_tab[i];
    accum = '0';

    while (in >= tenpow) {
      in -= tenpow;
      accum++;
    }

    *out++ = accum;

  } while (i --> 0);

  *out = '\0';
}

const static uint32_t i32_tab[10] = {
           1u,
          10u,
         100u,
        1000u,
       10000u,
      100000u, /* 10^ 5 */
     1000000u,
    10000000u,
   100000000u,
  1000000000u, /* 10^9  */
};

void uint32_to_string(char *out, uint32_t in) {
  int i;
  uint32_t tenpow;
  char accum;

  for (i = 9;i > 0;i--)
    if (in >= i32_tab[i]) break;

  do {
    tenpow = i32_tab[i];
    accum = '0';

    while (in >= tenpow) {
      in -= tenpow;
      accum++;
    }

    *out++ = accum;

  } while (i --> 0);

  *out = '\0';
}

4 个答案:

答案 0 :(得分:2)

我认为通过常数进行整数除法与进行乘法一样快,因为编译器将整数除法优化为常数除数的整数乘法。这是大多数优化编译器执行的重型数学技巧。

答案 1 :(得分:2)

除最简单(例如8位)微控制器之外的所有微控制器的最快方法是使用除法,但通过一次生成几个数字来减少除法数。

您会在我的问题here的答案中找到非常高度优化的代码。在C中使用它应该是一个简单的编辑,以消除std::string - 实际转换中没有使用C ++功能。核心是

while(val>=100)
{
   int pos = val % 100;
   val /= 100;
   *(short*)(c-1)=*(short*)(digit_pairs+2*pos); // or use memcpy
   c-=2;
}
while(val>0)
{
    *c--='0' + (val % 10);
    val /= 10;
}

我还为8位微处理器提供了一个优化的无分区代码,类似于问题代码中显示的思想,但没有循环。它最终得到了很多像这样的代码:

    if (val >= 80) {
        ch |= '8';
        val -= 80;
    }
    else if (val >= 40) {
        ch |= '4';
        val -= 40;
    }
    if (val >= 20) {
        ch |= '2';
        val -= 20;
    }
    if (val >= 10) {
        ch |= '1';
        val -= 10;
    }

答案 2 :(得分:1)

通常最快的方法是索引到一个足够大的字符串指针数组。一个数组查找,一个指针解除引用。尽管如此......内存使用量很大......这就是工程权衡的本质。速度有多快?

答案 3 :(得分:1)

printf的MS版本以“天真”的方式(在根据可选标志设置一堆变量之后):

            while (precision-- > 0 || number != 0) {
                digit = (int)(number % radix) + '0';
                number /= radix;                /* reduce number */
                if (digit > '9') {
                    /* a hex digit, make it a letter */
                    digit += hexadd;
                }
                *text.sz-- = (char)digit;       /* store the digit */
            }