必ず数字が引数に渡されるという限定した状況に限り使用可能な、atoiとatof(数字文字列を数値に変換するC言語関数)を作ってみました。
作ってみたっていうか、こちらの丸パクリして、ほんのちょっとさらに高速にしてみただけですけど。
fast_atof32は小数点以下9桁までしか扱えませんが、整数演算なのでちょっと早いはず。
桁数が少ない場合に使えるかなと。あと浮動小数演算が苦手なCPU向け。
//スペースを取って、符号を返す
int _space_sign(const char *s, const char **endptr)
{
while (*s == 0x20)
++s;
int sign = 0;
switch (*s)
{
case '-':
sign = -1;
// fall through
case '+':
++s;
break;
}
*endptr = s;
return sign;
}
//高速版atoi
int fast_atoi(const char *s)
{
int sign = _space_sign(s, &s);
int result = 0;
while(true)
{
if(*s > '9' || *s < '0')break;
result = result * 10 + *s - '0';
s++;
}
if (sign != 0)
result = -result;
return result;
}
//高速版atof
float fast_atof(const char *s)
{
int sign = _space_sign(s, &s);
float result = 0;
float cnm = 1;
while(true)
{
if(*s == '.')
{
s++;
break;
}else if(*s > '9' || *s < '0')break;
result = result * 10 + *s - '0';
s++;
}
while(true)
{
if(*s > '9' || *s < '0')break;
result = result * 10 + *s - '0';
cnm*=0.1;
s++;
}
if (sign != 0)
result = -result;
return result * cnm;
}
//高速版atof(桁が少ない)
float fast_atof32(const char *s)
{
int sign = _space_sign(s, &s);
int result = 0;
int cnm = 1;
while(true)
{
if(*s == '.')
{
s++;
break;
}else if(*s > '9' || *s < '0')break;
result = result * 10 + *s - '0';
s++;
}
while(true)
{
if(*s > '9' || *s < '0')break;
result = result * 10 + *s - '0';
cnm*=10;
s++;
}
if (sign != 0)
result = -result;
return (float)result / cnm;
} パクリ元との違いはisdigitやisspaceを単純に手書きしただけなんだけど、関数呼び出しを止めるだけでほんのちょっとだけ早くなる。
空白の無い、正の数しかない数字文字列に限定すれば、_space_signも要らなくなるからさらにちょっと早くなる。
ベンチマークを取ってみた。
atoi用データは”123456″
atof用データは””0.01111111″
10,000 * 10,000のループで測定。
VC++2008でビルド。
CPUはCore2Duo E6600 2.4GHz
| 最適化 有無 | 標準atoi | strtol | fast_atoi | 標準atof | strtod | fast_atof | fast_atof32 |
|---|---|---|---|---|---|---|---|
| なし | 5,732 | 5,281 | 4,091 | 55,496 | 55,300 | 8,698 | 6,620 |
| あり | 5,521 | 5,074 | 1,237 | 55,746 | 57,190 | 5,287 | 3,192 |
数値の単位はミリ秒。
最適化ありのほうはCPUによって違ってくるだろうけど、それにしたって標準関数に比べて早すぎな気がするなぁ。
最適化によってループが省略されたとか?
特にfalst_atofは標準atofに比べて10倍。fast_atof32だと17倍早い。劇的過ぎる。
でもfast_atof32とfast_atofの差は微妙だなぁ。1.6倍か。・・・fast_atof32要らないかも。使いづらいし。
strtolは機能が多いのにatoiより速いんだね。atoi良いとこなし。涙目。
逆にstrtodは微妙にatofに負けてるけど、その差は小さい。つか最適化掛けた方が遅いってどゆこと。
数値変換にsscanfを使う人がいるから、sscanfでも同じ条件で計ってみたんだけど、時間掛かりすぎてたから途中で止めた。
1,000 * 1,000のループにしたら1,000ms位だったから、単純に100,000ms掛かるわけだね。標準atofの2倍。
sscanfは使いやすいけど、一つ変換したいだけなら、素直にatoi/atof使った方がいいっぽいね。
移植性とか安全性とか考えてないので、使い場面は限られますね。
ゲームみたいに、異常データが来ないことが決まっていて、速度が求められるようなケースには結構有効なんじゃないかな。