在 C 中进行二进制算术的最佳方法?

发布于 2024-07-15 18:47:29 字数 384 浏览 5 评论 0原文

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

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

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

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

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

I am learning C and writing a simple program that will take 2 string values assumed to each be binary numbers and perform an arithmetic operation according to user selection:

  • Add the two values,
  • Subtract input 2 from input 1, or
  • Multiply the two values.

My implementation assumes each character in the string is a binary bit, e.g. char bin5 = "0101";, but it seems too naive an approach to parse through the string a character at a time. Ideally, I would want to work with the binary values directly.

What is the most efficient way to do this in C? Is there a better way to treat the input as binary values rather than scanf() and get each bit from the string?

I did some research but I didn't find any approach that was obviously better from the perspective of a beginner. Any suggestions would be appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

舞袖。长 2024-07-22 18:47:29

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

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

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

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

解决方案:
看一下 strtol() 手册页

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

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

#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 转换二进制文字。

Advice:
There's not much that's obviously better than marching through the string a character at a time and making sure the user entered only ones and zeros. Keep in mind that even though you could write a really fast assembly routine if you assume everything is 1 or 0, you don't really want to do that. The user could enter anything, and you'd like to be able to tell them if they screwed up or not.

It's true that this seems mind-bogglingly slow compared to the couple cycles it probably takes to add the actual numbers, but does it really matter if you get your answer in a nanosecond or a millisecond? Humans can only detect 30 milliseconds of latency anyway.

Finally, it already takes far longer to get input from the user and write output to the screen than it does to parse the string or add the numbers, so your algorithm is hardly the bottleneck here. Save your fancy optimizations for things that are actually computationally intensive :-).

What you should focus on here is making the task less manpower-intensive. And, it turns out someone already did that for you.

Solution:
Take a look at the strtol() manpage:

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

This will let you convert a string (nptr) in any base to a long. It checks errors, too. Sample usage for converting a binary string:

#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.
}

Supplying base 2 tells strtol to convert binary literals.

烧了回忆取暖 2024-07-22 18:47:29

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

但是既然你正在学习 CI 做了一个没有 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;
}

First out I do recommend that you use stuff like strtol as recommended by tgamblin,
it's better to use things that the lib gives to you instead of creating the wheel over and over again.

But since you are learning C I did a little version without strtol,
it's neither fast or safe but I did play a little with the bit manipulation as a example.

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;
}
谁的年少不轻狂 2024-07-22 18:47:29

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

例如,像 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),但这将需要相对昂贵的检查,而这是不必要的。

In order to get the best performance, you need to distinguish between trusted and untrusted input to your functions.

For example, a function like getBinNum() which accepts input from the user should be checked for valid characters and compressed to remove leading zeroes. First, we'll show a general purpose in-place compression function:

// 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");
}

Then provide a checker function that returns either the passed in value, or NULL if it was invalid:

// 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;
}

Then the input function itself:

#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;
}

Other functions to add or multiply should be written to assume the input is already valid since it will have been created by one of the functions in this library. I won't provide the code for them since it's not relevant to the question:

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

If the user chooses to source their data from somewhere other than getBinNum(), you could allow them to call checkBinNum() to validate it.

If you were really paranoid, you could check every number passed in to your routines and act accordingly (return NULL), but that would require relatively expensive checks that aren't necessary.

旧街凉风 2024-07-22 18:47:29

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

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

Wouldn't it be easier to parse the strings into integers, and then perform your maths on the integers?

I'm assuming this is a school assignment, but i'm upvoting you because you appear to be giving it a good effort.

野味少女 2024-07-22 18:47:29

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

Assuming that a string is a binary number simply because it consists only of digits from the set {0,1} is dangerous. For example, when your input is "11", the user may have meant eleven in decimal, not three in binary. It is this kind of carelessness that gives rise to horrible bugs. Your input is ambiguously incomplete and you should really request that the user specifies the base too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文