整数到字符串优化函数?

时间:2014-03-05 15:30:38

标签: c++ string optimization c++11 integer

我目前有这个函数将无符号整数转换为字符串(我需要一个适用于非标准类型的函数,如__uint128_t):

#include <iostream>
#include <algorithm>
#include <string>

template <typename UnsignedIntegral>
std::string stringify(UnsignedIntegral number, const unsigned int base)
{
    static const char zero = '0';
    static const char alpha = 'A';
    static const char ten = 10;
    std::string result;
    char remainder = 0;
    do {
        remainder = number%base;
        result += (remainder < ten) ? (zero+remainder) : (alpha+remainder-ten);
        number /= base;
    } while (number > 0);
    std::reverse(std::begin(result), std::end(result));
    return result;
}

int main()
{
   std::cout<<stringify(126349823, 2)<<std::endl;
   return 0;
}

有没有办法优化这段代码?

5 个答案:

答案 0 :(得分:6)

您可能想阅读Alexei Alexandrescu撰写的这篇文章,他在这里通过使用(fixed-radix)int来进行字符串转换来讨论低级优化:

https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920

请记住,优化时最重要的事情始终是分析。

答案 1 :(得分:1)

一个简单的事情是避免多个堆分配,可以通过result.reserve(CHAR_BIT * sizeof(Integral))(最大可能的字符串为base 2)或首先将字符串构建到本地数组然后创建std::string来完成从中。即便如此,我同意@SebastianRedl;你无法优化无测量。此外,您的代码不会考虑负数。

答案 2 :(得分:1)

如果你很幸运,你会在里面&#34;短串优化&#34;字符串的缓冲区大小。如果没有,那么你会产生动态内存分配,这可能至少比转换代码慢一个数量级。首先,摆脱std::string,并添加对确定合适的原始缓冲区大小的支持。

当您完成此操作后,请摆脱选择运算符导致的分支。表查找可能更快(或不)。但也可以使用比特技巧,例如通过右移位将小负数转换为全部,然后将其用作掩码。

最后,不是反转结果,而是可以从提供的缓冲区的末尾直接向后构建它,并生成指向start作为函数结果。


所有这些都说,请记住 MEASURE

对于逻辑上可能比原始情况差得多的优化,例如上面的测量,测量可能比简单地进行优化编码更有效。但是当你完成了显而易见的事情并且你想要获得最后一点性能时,测量是必要的。此外,对于大多数程序员来说,测量是必要的,只是为了不浪费时间进行完全不必要的优化,或引入新的低效率。

答案 3 :(得分:0)

您正在寻找一种优化代码的方法。确实有一种比纯数字转换更快的方法:你可以使用数字组,即在所需基数的基数中。

例如:

Base 2 - &gt;基数256(每次8位)

Base 8 - &gt;基数512(一次3个八进制数字)

基础10 - &gt;基数100(每次2位十进制数)

Base 16 - &gt;基数256(一次2个十六进制数字)

您需要将数字组合的表示预先制表为短ASCII字符串。并且您需要添加高阶数字的特殊处理以避免或撤消前导零。

OctalStrings[]= { "000", "001", "002" ... }

但主循环将保持以下形式:

do
  Q= N / Base
  R= N - Q * Base
  N= Q
  Insert(Strings[R])
while N>0

或者,对于二进制基数:

do
  R= N & (Base - 1)
  N= N >> LgBase
  Insert(Strings[R])
while N>0

你也可以通过预先计算基数的所有权力并使用商/余数来直接从左到右进行转换。

Base100Powers[]= { 1, 100, 10000, 1000000... }

do
  Q= N / Powers[k]
  N= N - Powers[k] * Q
  Append(Strings[Q])
  k--
while k>0

答案 4 :(得分:-1)

我认为,这是一个更高效的版本,我刚编码:

#include <iostream>
#include <type_traits>
#include <algorithm>
#include <string>
#include <array>

template <bool Upper = true, 
          typename Char = char,
          Char Zero = '0',
          Char Nine = '9',
          Char Alpha = Upper ? 'A' : 'a',
          Char Zeta = Upper ? 'Z' : 'z',
          Char One = 1,
          Char Ten = Nine-Zero+One,
          Char Size = (Nine-Zero+One)+(Zeta-Alpha+One),
          typename... Types, 
          class = typename std::enable_if<
          (std::is_convertible<Char, char>::value) && 
          (sizeof...(Types) == Size)>::type>
constexpr std::array<char, Size> alphabet(const Types&... values)
{
    return {{values...}};
}

template <bool Upper = true, 
          typename Char = char,
          Char Zero = '0',
          Char Nine = '9',
          Char Alpha = Upper ? 'A' : 'a',
          Char Zeta = Upper ? 'Z' : 'z',
          Char One = 1,
          Char Ten = Nine-Zero+One,
          Char Size = (Nine-Zero+One)+(Zeta-Alpha+One),
          typename... Types, 
          class = typename std::enable_if<
          (std::is_convertible<Char, char>::value) && 
          (sizeof...(Types) < Size)>::type>
constexpr std::array<char, Size> alphabet(Types&&... values)
{
    return alphabet<Upper, Char, Zero, Nine, Alpha, Zeta, One, Ten, Size>
           (std::forward<Types>(values)..., 
            Char(sizeof...(values) < Ten ? Zero+sizeof...(values)
                                         : Alpha+sizeof...(values)-Ten));
}

template <typename Integral,
          Integral Base = 10,
          Integral Zero = 0,
          Integral One = 1, 
          Integral Value = ~Zero,
          class = typename std::enable_if<
          (std::is_convertible<Integral, int>::value) && 
          (Value >= Zero) && 
          (Base > One)>::type>
constexpr Integral digits()
{
    return (Value != ~Zero)+
           (Value > Zero ? digits<Integral, Base, Zero, One, Value/Base>()
                         : Zero);
}

template <bool Upper = true,
          typename Integral,
          std::size_t Size = digits<Integral, 2>()>
std::string stringify(Integral number, const std::size_t base)
{
    static constexpr auto letters = alphabet<Upper>();
    std::array<char, Size+1> string = {};
    std::size_t i = 0;
    do {
        string[Size-i++] = letters[number%base];
    } while ((number /= base) > 0);
    return &string[Size+1-i];
}

int main()
{
    std::cout<<stringify(812723U, 16)<<std::endl;
    return 0;
}

使用Yves Daoust的技术(使用提供基础的功能的基础)可以更有效地优化它。