C 快速整数到十进制转换

C 快速整数到十进制转换,c,performance,data-conversion,C,Performance,Data Conversion,给定一个(无符号)整数,通常最快的转换方法是什么 最天真的方法是反复除以10,直到你达到零。我不喜欢这种方法,因为它 使用整数除法,这种除法速度慢,在某些集成平台上不可用 要求程序员随后翻转字符串。这使所需的内存操作数量增加了一倍 我想到了下面的方法将整数转换成十进制。这是个好主意吗?在像printf这样的函数的常见实现中是如何做到这一点的 #include <stdint.h> const static uint64_t i64_tab[20] = {

给定一个(无符号)整数,通常最快的转换方法是什么

最天真的方法是反复除以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';
}
#包括
常数静态uint64\U选项卡[20]={
1u,
10u,
100u,
1000u,
10000美元,
100000u,/*10^5*/
一百万,
1亿美元,
100000000美元,
100000000美元,
1000000000U,/*10^10*/
1000亿美元,
一亿,
1亿美元,
1000000000000美元,
10000000000000U,/*10^15*/
一亿,
1000000000000000U,
一亿,
100000000000000000U/*10^19*/
};
无效uint64到字符串(字符*输出,uint64输入){
int i;
uint64_t tenpow;
积炭;
对于(i=19;i>0;i--){
如果(in>=i64_tab[i])中断;
}
做{
tenpow=i64_tab[i];
累计='0';
while(in>=tenpow){
in-=tenpow;
accum++;
}
*out++=acum;
}而(i-->0);
*out='\0';
}
常量静态uint32\u t i32\u选项卡[10]={
1u,
10u,
100u,
1000u,
10000美元,
100000u,/*10^5*/
一百万,
1亿美元,
100000000美元,
1000000000u,/*10^9*/
};
无效uint32到字符串(字符*输出,uint32输入){
int i;
uint32_t tenpow;
积炭;
对于(i=9;i>0;i--)
如果(in>=i32_tab[i])中断;
做{
tenpow=i32_tab[i];
累计='0';
while(in>=tenpow){
in-=tenpow;
accum++;
}
*out++=acum;
}而(i-->0);
*out='\0';
}

我相信整数除以常数和乘法一样快,因为编译器优化了常数除数的整数除法到整数乘法。这是大多数优化编译器执行的一个繁重的数学技巧。

通常最快的方法是索引到足够大的字符串指针数组。一个数组查找,一个指针解引用。不过,它占用了大量内存。。。这就是工程权衡的本质。足够快的速度有多快?

微软版的printf采用了“天真”的方式(在基于可选标志设置了一系列变量之后):


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

您将在我的问题的答案中找到高度优化的代码。在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位micro提供了一个优化的无除法代码,类似于问题代码中显示的想法,但没有循环。它最终会产生很多类似这样的代码:

    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;
    }

“给定一个(无符号)整数,通常将其转换为整数的最快方法是什么?”您的意思是如果以字符串开头?因为将整数转换为整数的最快方法是什么也不做:)@fuzzxl对不起,我总是无意中进入C标记。@Seth没问题。我只是有点不喜欢“只使用X,我不在乎也不知道它能为谁工作”@fuzzxl这取决于你所说的“快”是什么意思。如果你真的想完成一些事情,使用内置函数是最快的。所以我想我会这样回答(虽然这不是C++,当时我不知道)。与此密切相关的是:整数乘法仍然非常慢,因为它是一种非流水线的微代码操作,这意味着整个OOO管道在执行时停止工作。有关x86,请参阅“英特尔64和IA-32体系结构优化参考手册”;根据经验,我可以告诉你,PPC更糟糕。@Crashworks,我没有这方面的经验,我尊重你的经验。这是非常令人惊讶的。像Core i7这样的顶级CPU呢?他们必须用管道输送MUL?@usr我不熟悉这种优化。你有推荐人吗?我唯一能想到的就是使用模逆运算,但这只适用于奇数。@BenVoigt这不适用于模乘吗?这个博客文章系列非常棒。我几天前读过,没有“足够快”的说法。表现最好的是“足够快”是的,有一个“足够快”,因为表现最好的通常是无法实现的,因为过高的资源需求。我的答案就是最好的例子。您是否有足够的内存用于2**64指针和相关字符串?可能不会。但我很有信心这是最好的。很好的反驳。在几乎所有的机器上,如果不缓存,内存访问速度会非常慢。因此,最大限度地减少内存访问次数可能是一个优势,这很有趣。我要写一个程序来比较这个方法和我的方法。@fuzzxl:我建议你点击进入另一个问题。已经有很多代码,包括性能数据,以及需要自己进行测试的基准代码。
    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;
    }