Лучший способ сделать двоичную арифметику в C?

Я изучаю C и пишу простую программу, которая будет принимать 2 строковых значения, которые считаются двоичными числами, и выполнять арифметическую операцию в соответствии с выбором пользователя:

  • добавить два значения,
  • вычесть вход 2 из входа 1 или
  • умножить два значения.

моя реализация предполагает, что каждый символ в строке-это двоичный разряд, например,char bin5 = "0101";, но это, кажется, слишком наивный подход, чтобы проанализировать строка символа за раз. В идеале, я хотел бы работать с двоичными значениями напрямую.

каков наиболее эффективный способ сделать это в C? Есть ли лучший способ рассматривать входные данные как двоичные значения, а не scanf() и получить каждый бит из строки?

Я сделал некоторые исследования, но я не нашел подход, который был явно лучше с точки зрения новичка. Любые предложения будут оценены!

5 ответов


Совет:
Не так много, что, очевидно, лучше, чем маршировать по строке символа за раз и убедиться, что пользователь ввел только единицы и нули. Имейте в виду, что, хотя вы могли бы написать действительно быстрая процедура сборки, если вы предположим все 1 или 0, вы действительно не хотите сделать это. Пользователь может ввести что угодно, и вы хотели бы иметь возможность сказать им, если они облажались или не.

это правда, что это кажется умопомрачительно медленным по сравнению с парой циклов, которые, вероятно, требуется, чтобы добавить фактические числа, но действительно ли имеет значение, если вы получите свой ответ в наносекунду или миллисекунду? Люди могут обнаружить только 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 для преобразования двоичных литералов.


во-первых, я рекомендую вам использовать такие вещи, как strtol, как рекомендовано tgamblin, лучше использовать то, что дает вам 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;
}

чтобы получить лучшую производительность, вам нужно различать доверенные и ненадежные входные данные для ваших функций.

например, такая функция, как 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 != '')
            *dst++ = *src++;
        *dst = '';
    }

    // Make zero if we removed the last zero.
    if (*num == '')
            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] = '';

    // 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), но это потребует относительно дорогостоящих проверок, которые не нужны.


не было бы проще разобрать строки на целые числа, а затем выполнить математику над целыми числами?

Я предполагаю, что это школьное задание, но я голосую за вас, потому что вы, кажется, прилагаете хорошие усилия.


предполагая, что строка является двоичным числом, просто потому, что он состоит только из цифр из множества {0,1} опасно. Например, когда вы вводите "11", пользователь может иметь в виду одиннадцать в десятичном, а не три в двоичном формате. Именно такая небрежность и порождает ужасных насекомых. Ваш ввод неоднозначно неполный, и вы действительно должны запросить, чтобы пользователь также указал базу.