将(const char *,size_t)表示的字符串转换为int的最快方法是什么?
字符串不以null结尾。 这两种方式都涉及到我想避免的字符串副本(以及更多)。
是的,这个功能每秒被称为几百万次。 :P
int to_int0(const char* c, size_t sz)
{
return atoi(std::string(c, sz).c_str());
}
int to_int1(const char* c, size_t sz)
{
return boost::lexical_cast<int>(std::string(c, sz));
}
答案 0 :(得分:3)
考虑到这样的计数字符串,您可以通过自己进行转换来获得一点速度。根据代码需要的强大程度,这可能相当困难。目前,让我们假设最简单的情况 - 我们确定字符串是有效的,只包含数字(现在没有负数),它代表的数字总是在int的范围内。对于那种情况:
int to_int2(char const *c, size_t sz) {
int retval = 0;
for (size_t i=0; i<sz; i++)
retval *= 10;
retval += c[i] -'0';
}
return retval;
}
从那里,你可以得到你想要的复杂 - 处理前导/尾随空格,' - '(但正确地对2的补码中的最大负数进行正确的操作并不总是微不足道的[编辑:见Nawaz的答案对于这个的一个解决方案]),数字分组等
答案 1 :(得分:3)
另一个慢版本,适用于uint32:
void str2uint_aux(unsigned& number, unsigned& overflowCtrl, const char*& ch)
{
unsigned digit = *ch - '0';
++ch;
number = number * 10 + digit;
unsigned overflow = (digit + (256 - 10)) >> 8;
// if digit < 10 then overflow == 0
overflowCtrl += overflow;
}
unsigned str2uint(const char* s, size_t n)
{
unsigned number = 0;
unsigned overflowCtrl = 0;
// for VC++10 the Duff's device is faster than loop
switch (n)
{
default:
throw std::invalid_argument(__FUNCTION__ " : `n' too big");
case 10: str2uint_aux(number, overflowCtrl, s);
case 9: str2uint_aux(number, overflowCtrl, s);
case 8: str2uint_aux(number, overflowCtrl, s);
case 7: str2uint_aux(number, overflowCtrl, s);
case 6: str2uint_aux(number, overflowCtrl, s);
case 5: str2uint_aux(number, overflowCtrl, s);
case 4: str2uint_aux(number, overflowCtrl, s);
case 3: str2uint_aux(number, overflowCtrl, s);
case 2: str2uint_aux(number, overflowCtrl, s);
case 1: str2uint_aux(number, overflowCtrl, s);
}
// here we can check that all chars were digits
if (overflowCtrl != 0)
throw std::invalid_argument(__FUNCTION__ " : `s' is not a number");
return number;
}
为什么它很慢?因为它逐个处理字符。如果我们保证我们可以访问最多s+16
的字节,我们就可以使用*ch - '0'
和digit + 246
的矢量化。
就像在这段代码中一样:
uint32_t digitsPack = *(uint32_t*)s - '0000';
overflowCtrl |= digitsPack | (digitsPack + 0x06060606); // if one byte is not in range [0;10), high nibble will be non-zero
number = number * 10 + (digitsPack >> 24) & 0xFF;
number = number * 10 + (digitsPack >> 16) & 0xFF;
number = number * 10 + (digitsPack >> 8) & 0xFF;
number = number * 10 + digitsPack & 0xFF;
s += 4;
范围检查的小更新:
第一个片段在每次迭代时都有冗余移位(或mov
),所以它应该是
unsigned digit = *s - '0';
overflowCtrl |= (digit + 256 - 10);
...
if (overflowCtrl >> 8 != 0) throw ...
答案 2 :(得分:2)
最快:
int to_int(char const *s, size_t count)
{
int result = 0;
size_t i = 0 ;
if ( s[0] == '+' || s[0] == '-' )
++i;
while(i < count)
{
if ( s[i] >= '0' && s[i] <= '9' )
{
//see Jerry's comments for explanation why I do this
int value = (s[0] == '-') ? ('0' - s[i] ) : (s[i]-'0');
result = result * 10 + value;
}
else
throw std::invalid_argument("invalid input string");
i++;
}
return result;
}
由于在上面的代码中,比较(s[0] == '-')
在每次迭代中完成,我们可以通过将result
计算为循环中的负数来避免这种情况,然后返回result
if { {1}}确实是s[0]
,否则返回'-'
(这使得它成为正数,应该是这样):
-result
这是一项改进!
在C ++ 11中,您可以使用std::stoi
系列中的任何函数。还有std::to_string
家庭。
答案 3 :(得分:1)
llvm::StringRef s(c,sz);
int n;
s.getAsInteger(10,n);
return n;
http://llvm.org/docs/doxygen/html/classllvm_1_1StringRef.html
答案 4 :(得分:0)
如果你经常运行这个功能,我打赌你会多次解析相同的数字。我的建议是将字符串BCD编码为一个静态字符缓冲区(你知道它不会很长,因为atoi
只能处理+ -2G)当小于X位时(32位X = 8)查找,对于64位查找,X = 16)然后将缓存放在哈希映射中。
当你完成第一个版本时,你可能会找到很好的优化,比如完全跳过BCD编码,只是在字符串中使用X字符(当字符串长度为&lt; = X时),以便在哈希中查找表。如果字符串较长,则回退到atoi
。
修改:...或者回退而不是atoi到Jerry Coffin的解决方案,这个解决方案的速度和它们一样快。
答案 5 :(得分:0)
如果您没有设置避免字符串副本,则必须编写自定义例程或使用第三方库。
你可能不想从头开始编写atoi(这里仍然可以制作一个bug),所以我建议从公共域或BSD许可代码中获取现有的atoi并进行修改。例如,您可以从FreeBSD cvs tree获取现有的atoi。