我目前有这个函数将无符号整数转换为字符串(我需要一个适用于非标准类型的函数,如__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;
}
有没有办法优化这段代码?
答案 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的技术(使用提供基础的功能的基础)可以更有效地优化它。