Битовый алгоритм деления со знаком в C - PullRequest
8 голосов
/ 09 июня 2011

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

Дивиденд - это тот, у которого абсолютное значение больше, а у делителя абсолютное значение меньше.

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

Я попытался реализовать это по схеме, упомянутой в первом ответе: Реализация деления с побитовым оператором По какой-то причине это не сильно меня задело.

Тогда я нашел это: http://www4.wittenberg.edu/academics/mathcomp/shelburne/comp255/notes/BinaryDivision.pdf Я заработал, когда написал код ниже, используя пример в конце документа.

Это правильно, если первое значение положительное, а второе - нет.

Я работал над этим не менее 2 дней. Может быть, кто-то может сказать, где я иду не так.


Вот код, который мне удалось собрать с помощью @Dysaster. Это не работает, когда оба значения являются либо отрицательными, либо положительными, но мне удалось получить 20 баллов из 25 за это на защите.

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

char *bits(char Rg) {

    unsigned char bit = 0x80;
    int i;
    char *bits;
    bits = (char*) malloc(9);
    for (i=0; i < 8; i++) {
        *(bits+i) = Rg & bit ? '1' : '0';
        bit >>= 1;
    }
    *(bits+i) = '\0';
    return bits;
}

int divide(char Rg1, char Rg2) {

    char Rg3, r=0;
    int i;

    printf("Rg1 : %s (%2d)\n", bits(Rg1), Rg1);
    printf("Rg2 : %s (%2d)\n", bits(Rg2), Rg2);
    Rg3 = Rg1;
    printf("Rg3 : %s (%2d)  <- copy of Rg1\n", bits(Rg3), Rg3);
    if (Rg1 < 0) {
        r = 0xff;
    }
    printf("rem : %s (%2d)  <- remainder after sign check\n", bits(r), r);

    for (i = 0; i < 8; i++) {

        printf("\n ------------ %d. ITERATION ------------\n", i+1);


        if (Rg3 & 0x80) {
            printf(" - left shift r and Rg3, carry\n");
            Rg3 <<= 1;
            r <<= 1;
            r += 1;
            printf(" > %s (%2d) | %s (%2d)\n", bits(r), r, bits(Rg3), Rg3);
        } else {
            printf(" - left shift r and Rg3\n");
            Rg3 <<= 1;
            r <<= 1;
            printf(" > %s (%2d) | %s (%2d)\n", bits(r), r, bits(Rg3), Rg3);
        }

        printf(" - add in the divisor\n");
        r += Rg2;
        printf(" > %s (%2d) | %s (%2d)\n", bits(r), r, bits(Rg3), Rg3);

        if (Rg1 < 0 && Rg2 > 0 && r >= 0 || Rg1 > 0 && Rg2 < 0 && r < 0) { // real ugly, I know
            printf(" - subtract the divisor and set the lowest bit of Rg3 to 1\n");
            r -= Rg2;
            Rg3 |= 0x01;
            printf(" > %s (%2d) | %s (%2d)\n", bits(r), r, bits(Rg3), Rg3);
        } else {
            printf(" - lowest bit of Rg3 stays 0\n");
            printf(" > %s (%2d) | %s (%2d)\n", bits(r), r, bits(Rg3), Rg3);
        }

    }

    // post division sign check
    if ((Rg1 < 0 && Rg2 > 0) || (Rg1 > 0 && Rg2 < 0)) {
        Rg3++;
    }

    printf("\n%s (%d) / %s (%d) = %s (%d) r %s (%d)\n\n", bits(Rg1), Rg1, bits(Rg2), Rg2, bits(Rg3), Rg3, bits(r), r);
}


int main(int argc, char *argv[]) {

    divide(-13, -4); // buggy
    divide(-29, 4);  // OK
    divide(19, -8);  // OK
    divide(17, 5);   // buggy

    return 0;
}

1 Ответ

2 голосов
/ 10 июня 2011

Похоже, ограничение, не позволяющее вам принимать абсолютные значения, является большим. Можно немного изменить код, который у вас есть, чтобы обрабатывать регистр Rg1> 0 и Rg2 <0. </p>

Вместо того, чтобы принимать абсолютное значение отрицательного числа, вы просто меняете знаки в местах, где используется Rg2, а также меняете знаки на выходе. Вы, кажется, начали именно так, но забыли о том, что немного отрицаете свой делитель (что осталось в Rg3 после того, как вы закончите). Вы можете сделать это двумя способами: оставить алгоритм как есть, но после восьми итераций установить Rg3=(Rg3^0xff + 1). В качестве альтернативы, вместо сдвига в 0 для отрицательного значения и 1 для положительного, вы можете вернуть его в основной цикл, сдвинув в 1, если r отрицательно, и 0 в противном случае (это эквивалентно неявному вычислению Rg3 ^ 0xff) и добавить 1 после восьми итераций. Чтобы понять, зачем вам нужен плюс 1, разделите 1 на -2, и вы увидите, что r всегда остается отрицательным, в результате чего все 1 s сдвигаются в Rg3. После восьми итераций у вас будет 0xff (или -1), но оно должно быть равно 0. Итак, вы добавляете 1.

Кстати, в функции bits есть ошибка. Строка char bit = 0x80 должна читать unsigned char bit = 0x80, потому что знаковый символ со значением 0x80 становится 0xC0 при смещении вправо - и это портит ваши битовые значения.

В любом случае. Я не знаю, как обращаться со случаем Rg1<0 независимо от знака Rg2. Я обновлю ответ, если смогу придумать что-нибудь. В конце ваше подразделение должно будет выбрать один из четырех алгоритмов, чтобы выполнить работу, в зависимости от знака каждого входного параметра.


EDIT:

Я не уверен, как это объяснить точно, но для случая Rg1<0, Rg2>0 решение состоит в том, чтобы просто изменить начальное значение r на 0xff и изменить проверку знака r внизу до r >= 0. Результат для -19/8 равен -2*8-3, а для -29/4 равен -7*4-1. Если вы хотите, чтобы остаток был всегда положительным, вам нужно вычесть 1 из Rg3 и добавить Rg2 к r.

Я выбрал 0xFF начальное значение, потому что r - это просто расширение знака Rg1 до 16 бит. Поскольку r теперь всегда является отрицательным, проверка, становится ли оно нулевым или положительным после добавления Rg2, является естественным.

Вы должны быть в состоянии справиться со случаем Rg1<0, Rg2<0 довольно легко: просто снова верните знаки операций Rg2. Также возможно объединить четыре различных подпрограммы в одну, которая обрабатывает все четыре случая, но я оставлю это на ваше усмотрение. :)

...