【问题标题】:Best way to do binary arithmetic in C?在C中进行二进制算术的最佳方法?
【发布时间】:2010-10-14 21:51:47
【问题描述】:

我正在学习 C 并编写一个简单的程序,该程序将采用 2 个假定为二进制数的字符串值并根据用户选择执行算术运算:

  • 将这两个值相加,
  • 从输入 1 中减去输入 2,或
  • 将这两个值相乘。

我的实现假设字符串中的每个字符都是二进制位,例如char bin5 = "0101";,但是一次解析字符串一个字符的方法似乎太天真了。理想情况下,我想直接使用二进制值。

在 C 中最有效的方法是什么?有没有更好的方法将输入视为二进制值而不是scanf() 并从字符串中获取每一位?

我做了一些研究,但从初学者的角度来看,我没有发现任何明显更好的方法。任何建议将不胜感激!

【问题讨论】:

    标签: c binary performance math processing-efficiency


    【解决方案1】:

    将字符串解析为整数,然后对整数进行数学运算不是更容易吗?

    我假设这是一项学校作业,但我赞成你,因为你似乎付出了很大的努力。

    【讨论】:

    • 这实际上是我所做的。由于我找不到在数字数据类型中以二进制基数表示数字的方法(与十六进制的 0xFF 不同),因此我正在对十进制数执行操作并将其转换回二进制值。我的问题的起源与作业有关,但现在已经完成了。
    【解决方案2】:

    建议:
    没有什么比一次遍历一个字符并确保用户只输入一个和零更好的了。请记住,如果您假设所有内容都是10,即使您可以编写一个非常 快速的汇编程序,但您并不想这样做.用户可以输入任何内容,并且您希望能够告诉他们他们是否搞砸了。

    确实,与添加实际数字可能需要的几个周期相比,这看起来慢得令人难以置信,但是如果您在纳秒或毫秒内得到答案真的很重要吗?无论如何,人类只能检测到 30 毫秒的延迟。

    最后,与解析字符串或添加数字相比,从用户那里获取输入并将输出写入屏幕所花费的时间要长得多,因此您的算法几乎不是这里的瓶颈。为实际上计算密集型的事物保存您的花哨优化:-)。

    您在这里应该关注的是减少任务的人力密集度。而且,事实证明已经有人为你做了。

    解决方案:
    看看the strtol() manpage

    long strtol(const char *nptr, char **endptr, int base);
    

    这将允许您将任何基数的字符串 (nptr) 转换为长字符串。它也检查错误。转换二进制字符串的示例用法:

    #include <stdlib.h>
    
    char buf[MAX_BUF];
    get_some_input(buf);
    
    char *err;
    long number = strtol(buf, &err, 2);
    if (*err) {
        // bad input: try again?
    } else {
        // number is now a long converted from a valid binary string.
    }
    

    提供基数 2 告诉 strtol 转换二进制文字。

    【讨论】:

    • 负二进制数字符串不常见,所以你可能想使用 strtoul(3)
    【解决方案3】:

    首先,我确实建议您使用 tgamblin 推荐的 strtol 之类的东西, 最好使用 lib 提供给您的东西,而不是一遍又一遍地创建轮子。

    但是由于你正在学习 C 我做了一个没有 strtol 的小版本, 它既不快速也不安全,但我确实以位操作为例进行了一些尝试。

    int main()
    {
        unsigned int data = 0;
        int i = 0;
    
        char str[] = "1001";
    
        char* pos;
        pos = &str[strlen(str)-1];
    
        while(*pos == '0' || *pos == '1')
        {
            (*pos) -= '0';
            data += (*pos) << i;
    
            i++;
            pos--;
        }
    
        printf("data %d\n", data);
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      为了获得最佳性能,您需要区分函数的可信输入和不可信输入。

      例如,像getBinNum() 这样接受用户输入的函数应该检查有效字符并压缩以删除前导零。首先,我们将展示一个通用的就地压缩函数:

      // General purpose compression removes leading zeroes.
      void compBinNum (char *num) {
          char *src, *dst;
      
          // Find first non-'0' and move chars if there are leading '0' chars.
          for (src = dst = num; *src == '0'; src++);
          if (src != dst) {
              while (*src != '\0')
                  *dst++ = *src++;
              *dst = '\0';
          }
      
          // Make zero if we removed the last zero.
          if (*num == '\0')
                  strcpy (num, "0");
      }
      

      然后提供一个检查器函数,该函数返回传入的值,如果无效则返回 NULL:

      // Check untested number, return NULL if bad.
      char *checkBinNum (char *num) {
          char *ptr;
      
          // Check for valid number.
          for (ptr = num; *ptr == '0'; ptr++)
              if ((*ptr != '1') && (*ptr != '0'))
                  return NULL;
      
          return num;
      }
      

      然后是输入函数本身:

      #define MAXBIN 256
      
      // Get number from (untrusted) user, return NULL if bad.
      char *getBinNum (char *prompt) {
          char *num, *ptr;
      
          // Allocate space for the number.
          if ((num = malloc (MAXBIN)) == NULL)
              return NULL;
      
          // Get the number from the user.
          printf ("%s: ", prompt);
          if (fgets (num, MAXBIN, stdin) == NULL) {
              free (num);
              return NULL;
          }
      
          // Remove newline if there.
          if (num[strlen (num) - 1] == '\n')
              num[strlen (num) - 1] = '\0';
      
          // Check for valid number then compress.
          if (checkBinNum (num) == NULL) {
              free (num);
              return NULL;
          }
          compBinNum (num);
      
          return num;
      }
      

      应该编写其他要相加或相乘的函数,以假设输入已经有效,因为它是由该库中的一个函数创建的。我不会为他们提供代码,因为它与问题无关:

      char *addBinNum (char *num1, char *num2) {...}
      char *mulBinNum (char *num1, char *num2) {...}
      

      如果用户选择从getBinNum() 以外的其他地方获取数据,您可以允许他们调用checkBinNum() 来验证它。

      如果你真的很偏执,你可以检查传入你的例程的每个数字并采取相应的行动(返回 NULL),但这需要相对昂贵的检查,而且没有必要。

      【讨论】:

        【解决方案5】:

        仅仅因为字符串仅由集合 {0,1} 中的数字组成而假设字符串是二进制数是危险的。例如,当您的输入是“11”时,用户可能指的是十进制的 11,而不是二进制的 3。正是这种粗心大意导致了可怕的错误。您的输入不完整,您确实应该要求用户也指定基数。

        【讨论】:

        • 附言。通常,当人们看到或输入 11 时,他们更可能表示 11 而非 3,而且旧习惯很难改掉。
        • 好点。如果我正在制作一个通用脚本,允许用户选择一个选择的基数将是减少输入错误的理想选择。在这个特定的用例中,二进制值仅用于输入是公认的。也就是说,在可能出现输入歧义的情况下,最好考虑特殊性。
        猜你喜欢
        • 2021-12-15
        • 2011-01-25
        • 2014-01-04
        • 1970-01-01
        • 1970-01-01
        • 2013-02-27
        • 1970-01-01
        • 1970-01-01
        • 2012-10-23
        相关资源
        最近更新 更多