如何从标准输入获取整数?快速地。可以用准确性换取性能

How to get an integer from stdin? Fast. Can trade accuracy for performance

我正在努力想出一个快速简单的方法

"Get a string from stdin and convert to an integer. If you can't, just pretend we got zero".

这是一个 Linux 嵌入式系统,CPU 和内存非常宝贵。性能很重要,准确性不是那么重要。这应该能够每秒进行多次摄取。我最终会把它变成一个守护进程并将最新的 1024 个值存储在一个数组中。

这是我使用 atoi 的结果:

#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[] ) {
  char *c = argv[1];
  unsigned int i = 1; /* on atoi() failure, i = 0 */

  if (i = atoi(c)) {
      puts ("atoi() success");
  }
  else {
      puts ("atoi() FAILED");
  }

  printf("argv[1] = %s\n", argv[1]);
  printf("      i = %d\n", i);
}

一些测试运行/模糊测试:

# ./test_atoi 3
atoi() success
argv[1] = 3
      i = 3

# ./test_atoi 99999999999999999999
atoi() success
argv[1] = 99999999999999999999
      i = 2147483647

# ./test_atoi 3.14159
atoi() success
argv[1] = 3.14159
      i = 3

# ./test_atoi $(echo -ne "\u2605")
atoi() FAILED
argv[1] = ★
      i = 0

这失败了:

# ./test_atoi $(echo -e "[=12=]")
Segmentation fault

然后我会添加一个 NUL 检查:

if (argv[1] == '[=13=]') {
    i = 0;
}

这就够了吗?我刚刚(糟糕地)重新实现了 strtol 吗? 我应该继续使用 strtol 吗?如果是,我应该检查什么,strtol 还没有?

我真正关心的不是因为输入错误而死亡。我可以愉快地忍受偶尔从转换中得到垃圾。

编辑:int i = 1 只是因为我想看看 atoi() 是否使它变为 0。

随着时间的推移对贫民窟进行剖析

编辑:我删除了 print 语句并将从 stdin 读取的内容包装到 for 循环中的 atoi/strtol 中。

# time seq 0 999888 | ./test_atoi
real    0m5.245s
user    0m5.870s
sys     0m0.030s

# time seq 0 999888 | ./test_atoi
real    0m5.230s
user    0m5.960s
sys     0m0.050s

# time seq 0 999888 | ./test_atoi
real    0m5.395s
user    0m5.920s
sys     0m0.080s

# time seq 0 999888 | ./test_strtol    
real    0m5.332s
user    0m5.860s
sys     0m0.030s

# time seq 0 999888 | ./test_strtol
real    0m5.023s
user    0m5.790s
sys     0m0.060s

# time seq 0 999888 | ./test_strtol
real    0m5.286s
user    0m5.970s
sys     0m0.010s

好吧,这太疯狂了。我应该用我的时间和你的时间做些更有成效的事情!

This is a Linux embedded system, CPU and memory are at a premium.

是的。呃,不。如果你是 运行 一个普通的 linux,你的内核将在几千个地方使用 atoi 和反函数。你的单一数字解析器几乎不会产生任何影响,除非你打算每秒调用它几千次......

Should i just go ahead and use strtol?

出于上述原因:是的。

If yes, anything i should be checking for, that strtol isn't already?

您应该检查 strtol 的 return 值。我真的不同意你的 "don't need precision" 方法。像这样的事情要么做对了,要么就大错特错了。

编辑 你说:

don't need precision = i only care about values 0 - 100

这意味着 a) 您只需要 atoi,而不是 atol/strtol;在那里,节省了 CPU 个周期。接下来您是否真的需要将看起来像 13.288 的字符串转换为整数,或者您是否可以假设所有字符串的长度都是 1 到 3 个字符?在那种情况下,对于原始性能,可能

inline unsigned char char2digit(const char *c) {
    unsigned char v = *c - '0';
    return (v<1 || v>9)? 0 : v;
}
inline signed char characters2number(const char *string)
{
    size_t len = strnlen(string,4);
    if(len < 1 || len > 3)
        return -1;
    signed char val = 0;
    signed char power_of_ten = 1;
    for(unsigned char idx = 1; idx <= len; ++idx)
    {
        signed char val += power_of_ten * char2digit(string + len - idx)
        power_of_ten *= 10;
    }
    return val;
}

我的意思是,如果你在烤面包机上。否则 atoi 会支持你。您可能仍想查看 strnlen.

main (int argc, char **argv)
{
     int i = 0;

     if (argc > 1)
         sscanf (argv [1], "%d", &i);

     printf ("i = %d\n", i);
}