【问题标题】:Fast integer to decimal conversion快速整数到十进制转换
【发布时间】:2012-05-16 08:06:35
【问题描述】:

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

这样做的简单方法是反复除以 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';
}

【问题讨论】:

  • “给定一个(无符号)整数,通常最快的将其转换为整数的方法是什么?”你的意思是如果你从一个字符串开始?因为将整数转换为整数的最快方法是什么都不做:)
  • @FUZxxl 对不起,我总是不小心进入C标签。
  • @Seth 没问题。我只是有点不喜欢“只使用 X,我不在乎也不知道它对谁有用”
  • @FUZxxl 这取决于您所说的“快速”是什么意思。如果你真的想完成某件事,使用内置函数是最快的。所以我想我会这样回答(虽然这不是我当时不知道的 C++)。没有伤害的意思。

标签: c performance data-conversion


【解决方案1】:

我相信整数除以常数与进行乘法运算一样快,因为编译器将整数除法优化为常数除数的整数乘法。这是大多数优化编译器执行的繁重数学技巧。

【讨论】:

  • 整数乘法仍然很慢,因为它是一种非流水线的微编码操作,这意味着整个 OOO 流水线在执行时会停止。请参阅 x86 的“英特尔® 64 和 IA-32 架构优化参考手册”;我可以根据经验告诉你,PPC 上的情况更糟。
  • @Crashworks,我没有这方面的经验,我尊重你的经验。这是非常令人惊讶的。像酷睿 i7 这样的顶级 CPU 怎么样?他们必须管道 muls?
  • @usr 我不熟悉这个优化。你有参考吗?我唯一能想到的是使用模逆,但这仅适用于奇数。
  • @BenVoigt 这不是模乘法吗?
  • ridiculousfish.com/blog/posts/labor-of-division-episode-i.html 这个博文系列非常棒。我几天前读过。
【解决方案2】:

通常最快的方法是索引到一个足够大的指向字符串的指针数组。一次数组查找,一次指针取消引用。但是,它对内存的使用很重......这就是工程权衡的本质。多快才够快?

【讨论】:

  • 没有“足够快”。表现最好的就是“足够快”
  • 是的,有一个“足够快”,因为由于资源需求过高,通常无法实现最佳性能。我的回答就是最好的例子。你有足够的内存来存放 2**64 指针和相关的字符串吗?可能不是。但我非常有信心这样做效果最好。
  • 很好的反驳。在几乎所有机器上,如果没有缓存,内存访问会非常慢。因此,尽量减少完成的内存访问次数可能是一个优势。
【解决方案3】:

MS 版本的 printf 以“幼稚”的方式进行(在基于可选标志设置了一堆变量之后):

            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 */
            }

【讨论】:

    【解决方案4】:

    除了最简单(例如 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;
        }
    

    【讨论】:

    • 这很有趣。我将编写一个程序来将这种方法与我的方法进行比较。
    • @FUZxxl:我建议您点击其他问题。已经有很多代码,带有性能数据,以及进行自己测试所需的基准代码。
    猜你喜欢
    • 1970-01-01
    • 2019-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-12
    • 2012-12-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多