String(const char *,size_t)到int?

时间:2012-03-08 15:43:30

标签: c++

将(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));
}

6 个答案:

答案 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。