[英]Conversion from string to const char* + size_t in ""operator
[英]String (const char*, size_t) to int?
將(const char *,size_t)表示的字符串轉換為int的最快方法是什么?
該字符串不是以空值結尾的。 這兩種方式都涉及一個字符串復制(以及更多),我想避免這樣做。
是的,此功能每秒被調用幾百萬次。 :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));
}
給定一個這樣的字符串,您可以自己進行轉換以加快速度。 但是,根據代碼需要具有的魯棒性,這可能相當困難。 目前,讓我們假設最簡單的情況-我們確定字符串是有效的,僅包含數字(目前沒有負數),並且它表示的數字始終在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的答案作為一種解決方案到此]),數字分組等。
對於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;
范圍檢查的小更新:
第一個代碼段在每次迭代中都有多余的shift(或mov
),因此應該
unsigned digit = *s - '0';
overflowCtrl |= (digit + 256 - 10);
...
if (overflowCtrl >> 8 != 0) throw ...
最快的:
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
計算為負數來避免這種情況,然后在s[0]
確實為真的情況下返回result
'-'
,否則返回-result
(使它成為正數,應該是):
int to_int(char const *s, size_t count)
{
size_t i = 0 ;
if ( s[0] == '+' || s[0] == '-' )
++i;
int result = 0;
while(i < count)
{
if ( s[i] >= '0' && s[i] <= '9' )
{
result = result * 10 - (s[i] - '0'); //assume negative number
}
else
throw std::invalid_argument("invalid input string");
i++;
}
return s[0] == '-' ? result : -result; //-result is positive!
}
那是一個進步!
但是在C ++ 11中,您可以使用std::stoi
系列中的任何函數。 還有std::to_string
家族。
llvm::StringRef s(c,sz);
int n;
s.getAsInteger(10,n);
return n;
http://llvm.org/docs/doxygen/html/classllvm_1_1StringRef.html
如果您經常運行該函數,我敢打賭您會多次解析相同的數字。 我的建議是在少於X個數字(32位查找時,X = 8,X = 8)時,將字符串BCD編碼到靜態char緩沖區中(您知道這不會太長,因為atoi
只能處理+ -2G)。 16(對於64位查找),然后將緩存放入哈希映射。
完成第一個版本后,您可能會發現不錯的優化方法,例如完全跳過BCD編碼,僅在字符串中使用X個字符(當字符串的長度<= X時)在哈希表中查找。 如果字符串較長,則回atoi
。
編輯 :...或回退,而不是傑瑞·科芬(Jerry Coffin)解決方案的解決方案,該解決方案很快就可以解決。
如果您死於避免字符串復制,則必須編寫自定義例程或使用3rd party庫。
您可能不希望從頭開始編寫atoi(仍然可能在這里犯一個錯誤),所以我建議您從公共領域或BSD許可的代碼中獲取現有的atoi並進行修改。 例如,您可以從FreeBSD cvs tree獲取現有的atoi。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.