Преобразование отрицательного десятичного числа в двоичную в C - PullRequest
0 голосов
/ 23 ноября 2018

В настоящее время я работаю над программой, которая (среди прочего) должна преобразовать десятичное число в двоичное, восьмеричное и шестнадцатеричное.Это уже работает с этим кодом:

    int e = 0;
    }
        while(i != 0){
            str[e] = (i%b) + '0';
            i = i / b;
            if(str[e] > '9'){
                str[e] = str[e] + 7;
            }
            e++;

    }
    if(vorzeichen == -1){
        str[e] = '1';
        e++;
    }
    if(b == 16){
        str[e] = 'x';
        str[e+1] = '0';
    }
    else if(b == 8){
        str[e] = '0';
    }
}

b - это база (2 для двоичного кода, 8 для восьмеричного и 16 для гекса), а i - это число, которое я хочу преобразовать.Это выдает строку символов, которые затем меняются местами, чтобы получить правильное число.Теперь, если я попробую это с отрицательными числами, он выдаст строки, содержащие не только 0 и 1, но также /, что равно «0» -1 в таблице ASCII.Для восьмеричных и десятичных чисел также выдаются символы ниже «/» в таблице ASCII.Я пробовал разные возможные решения, но ни одно из них не дало желаемого результата.В Интернете я читаю, что я должен использовать дополнение 2s, которое я застрял, пытаясь использовать его.Это просто не работает для меня.

Ответы [ 3 ]

0 голосов
/ 23 ноября 2018

При преобразовании между различными базами / основами всегда работайте с целыми типами без знака.

Допустим, у вас есть long num, который вы хотите преобразовать.Используйте unsigned long u.Чтобы представить отрицательные значения в формате дополнения до двух, вы можете использовать

if (num < 0)
    u = 1 + (~(unsigned long)(-num));
else
    u = num;

или даже короче,

unsigned long  u = (num < 0) ? 1 + (~(unsigned long)(-num)) : num;

Это работает на всех архитектурах (кроме num == LONG_MIN, в этом случаевыше это технически неопределенное поведение), даже те, которые не используют внутреннее дополнение до двух , потому что мы по существу преобразуем абсолютное значение в num.Если num изначально был отрицательным, мы затем дополняем их до значения без знака.


В комментарии chux предложил альтернативную форму, которая не полагается на UB для num == LONG_MIN (если только LONG_MAX == ULONG_MAX, что было бы ужасно странно видеть):

unsigned long  u = (num < 0) ? 1 + (~((unsigned long)(-1 - num) + 1)) : num;

Это может показаться "уродливым", но здравый компилятор C должен иметь возможность полностью оптимизировать любой из них на архитектурах с двумя целыми числами дополнения.Версия chux позволяет избежать неопределенного поведения, вычитая отрицательное значение num из -1, отображая таким образом -1 в 0, -2 в 1 и т. д., гарантируя, что все отрицательные значения представимы как неотрицательные long.Это значение затем преобразуется в unsigned long.Это увеличивается на единицу, чтобы учесть более ранние значения -1.Эта процедура дает правильное отрицание num.

Другими словами, чтобы получить абсолютное значение long, вы можете использовать

unsigned long  abs_long(const long  num)
{
    return (num < 0) ? (unsigned long)(-1 - num) + 1u : (unsigned long)num;
}
0 голосов
/ 23 ноября 2018

% - это функция остатка, а не mod .

При b==2, i%b возвращает [-1, 0, 1].Это не является необходимой функциональностью для str[e] = (i%b) + '0'; См. ... разница между «mod» и «Остаток»

Это причина '/', и «также выдает символы ниже'/' ".


Соберите строку из" правильного "

С дополнением 2 int, простой подход заключается в преобразованиидо unsigned и избежать отрицательного результата от %.Поскольку код использует % для извлечения наименьшей значащей цифры, переместитесь в буфер справа налево.

#include <limits.h>

...
unsigned u = i;

// make a temporary buffer large enough for any string output in binary
//           v------v Size of `u` in "bytes"
//           |      |   v------v Size of a "byte" - commonly 8
char my_buff[sizeof u & CHAR_BIT + 1];
int e = 0;

// Form a pointer to the end so code assigns the least significant digits on the right
char *p = &my_buff[sizeof my_buff - 1];

// Strings are null character terminated
*p = '\0';

// Use a `do` loop to insure at least one pass. Useful when `i==0` --> "0"
do {
  p--; 
  p[e] = "0123456789ABCDEF"[u%b];  // Select desired digit
  u = u / b;
} while (u);

// "prepend" characters as desired
if(b == 16){
  *(--p) = 'x';
  *(--p) = '0';
}
else if(b == 8 && i != 0){
  *(--p) = '0';
}

strcpy(str, p);
0 голосов
/ 23 ноября 2018

Если вы хотите отобразить отрицательное десятичное число, вы можете просто преобразовать int в unsigned int следующим образом:

unsigned int value = (unsigned int)i;

Теперь вам нужно только использовать value вместо iв вашей программе и все будет хорошо.Вот хорошее объяснение, почему: Преобразование отрицательного десятичного числа в двоичное

...