переполнение переменной при преобразовании двоичных чисел в десятичные

#c #binary

#c #двоичный

Вопрос:

Я кодирую программу, которая преобразует двоичные числа в десятичные. Когда я достигаю мощности 32, моя переменная переполняется, поскольку она не может содержать столько информации.

Что мне делать?

 unsigned long long conver( char *bin ){
    unsigned long long temp = 0;
    int i = 0;
    int x = 0;
    int j = 0;
    int n = 0;
 
    while (bin[i]!=''){
        x = 1;
        n  = 1;
          
        for (j = 0; j < i; j  ){
            x = x*2;
        }
      
        if (bin[i] == '0'){
            n = 0;
        }
          
        if (n == 1){
            if (bin[i] == '1'){
                temp = temp   x;
            }
        }
        i  ;
    }
    return temp;
}
  

Вот мои тестовые номера:

 "0",
"1",
"00",
"01",
"10",
"11",
"111",
"0111111111111111111111111111111111111111111111111111111111111111",
"1000000000000000000000000000000000000000000000000000000000000000",
"1111111111111111111111111111111111111111111111111111111111111111"
  

Комментарии:

1.Сделать x unsigned long long ?

2. В сторону: ваша функция немного неэффективна. Для каждой цифры нужно только умножить temp на 2 (или сдвинуть влево на 1) и добавить новую цифру, если она есть '1' .

3. A) Попробуйте дать вашим переменным значимые имена, чтобы мы могли понять их намерение. Б) Удалите лишние пустые строки, которые не служат никакой другой цели, кроме как скрыть ваш код под прокручиваемым разделом.

4. из замечания @IanAbbott почему вы вычисляете x , когда это бесполезно? почему нет ‘else’, а не для повторного тестирования? вам не нужны все эти вещи о n . Ваш код может быть в 5 раз короче

5. @bruno Idk почему насчет x это был очень хороший момент, и причина, по которой код был неэффективным / длинным, заключалась в том, что я переписывал это несколько раз, и у меня просто были копии, поэтому я копировал и вставлял фрагменты моих старых кодов и объединял их. Я собирался это очистить

Ответ №1:

Когда я достигаю степени 32, моя переменная переполняется, поскольку она не может содержать столько информации. Что мне делать?

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

Количество битов для неподписанного типа T задается :

  sizeof(T) * CHAR_BIT
  

Обратите внимание, что у вас есть типы, позволяющие иметь ожидаемое количество битов независимо от компилятора / процессора: uint32_t имеет 32b, uint64_t имеет 64b и т. Д


Из-за этого у вас есть несколько проблем в вашем коде, основная проблема заключается в том, что ваше преобразование неверно, например, ввод «100 … 000» вернет 1 независимо от числа 0, которое у вас есть, что из-за этого неправильного (и бесполезного) цикла :

     for (j = 0; j < i; j  ){
        x = x*2;
    }
  

который предполагает, что число в двоичном формате задается в обратном порядке, потому что считается, что самая первая цифра дает значение 1 или 0, вторая — значение 2 или 0, третья 4 или 0 и т.д.

Если вы хотите использовать этот цикл как есть, вам нужно пройти ввод от последнего символа до первого, а не от первого до последнего.


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

     n  = 1;
      
    ...compute x
  
    if (bin[i] == '0'){
        n = 0;
    }
      
    if (n == 1){
        if (bin[i] == '1'){
            temp = temp   x;
        }
    }
  

если (bin[i] == '0') значение true, n изменяется с 1 на 0, это означает, что невозможно иметь оба (bin[i] == '0') и (n == 1) поэтому if (n == 1) может быть заменено на else :

     n  = 1;
      
    ...compute x
  
    if (bin[i] == '0'){
        n = 0;
    }
    else {
        if (bin[i] == '1'){
            temp = temp   x;
        }
    }
  

теперь мы видим, что n только присваивается, но никогда не используется, поэтому его можно удалить :

     ...compute x
  
    if (bin[i] == '0'){
        /* nothing */
    }
    else {
        if (bin[i] == '1'){
            temp = temp   x;
        }
    }
  

тестировать (bin[i] == '0') явно бесполезно, потому что ничего не делается, когда это true, код теперь :

     ...compute x
    if (bin[i] == '1'){
      temp = temp   x;
    }
  

как вы можете видеть, значение x бесполезно, за исключением случаев, когда (bin[i] == '1') оно равно true, поэтому бесполезно вычислять его до того, как узнать (bin[i] == '1') , что это правда, поэтому :

 if (bin[i] == '1'){
  ...compute x
  temp = temp   x;
}
  

Из-за того, что значение x не является ожидаемым, обратите внимание, что в первом цикле оно равно 1, затем во втором цикле 2, затем 4 и т. Д., Поэтому вам не нужно выполнять внутренний цикл и просто инициализировать x с помощью 1 перед циклом, а затем умножить его на 2в конце цикла, и ваша функция теперь :

 unsigned long long conver( char *bin ){
    unsigned long long temp = 0;
    int i = 0;
    int x = 1;
 
    while (bin[i]!=''){
       if (bin[i] == '1'){
         temp  = x;
       }
       i  ;
       x *= 2;
    }
    return temp;
}
  

как вы можете видеть, код сильно сокращен по сравнению с вашим, даже если он делает то же самое.

Обратите внимание, что ваш код рассматривает любой символ, отличный от ‘1’, как ‘0’, поэтому «1az» считается «100».


Теперь правильный код (из-за использования strtoll или любой другой встроенной функции), ваша ошибка заключается в том, чтобы каждый раз умножать x на 2, а не умножать temp :

 unsigned long long conver( char *bin ){
    unsigned long long temp = 0;
    int i = 0;
 
    while (bin[i]!='') {
       temp *= 2;
       if (bin[i] == '1'){
         temp  = 1;
       }
       i  ;
    }
    return temp;
}
  

или более компактный :

 unsigned long long conver( char *bin ){
    unsigned long long temp = 0;
    int i = 0;
 
    while (bin[i]!='')
       temp = temp*2   (bin[i  ] == '1');
    return temp;
}
  

по-прежнему управление любым символом, отличным от ‘1’, как ‘0’.

Итак

 #include <stdio.h>

unsigned long long conver( char *bin ){
    unsigned long long temp = 0;
    int i = 0;
 
    while (bin[i]!='')
       temp = temp*2   (bin[i  ] == '1');
    return temp;
}

int main(int argc, char ** argv)
{
  while (--argc)
  {
    printf("%s => %llun", argv[1], conver(argv[1]));
    argv  = 1;
  }
  
  return 0;
}
  

Компиляция и выполнение:

 pi@raspberrypi:/tmp $ gcc -Wall c.c
pi@raspberrypi:/tmp $ ./a.out 1 100 110 1010101010
1 => 1
100 => 4
110 => 6
1010101010 => 682
pi@raspberrypi:/tmp $ 
  

Обратите внимание, что также возможно удалить переменную i:

 unsigned long long conver( char *bin ){
    unsigned long long temp = 0;
 
    while (*bin != '')
       temp = temp*2   (*bin   == '1');
    return temp;
}
  

и потому, что вы не изменяете содержимое bin :

 unsigned long long conver( const char *bin ){
    unsigned long long temp = 0;
 
    while (*bin != '')
       temp = temp*2   (*bin   == '1');
    return temp;
}
  

Ответ №2:

Если вам не нужно самостоятельно кодировать процедуру, вы можете использовать strtoull , которая может преобразовывать как минимум 64 битовые значения из двоичных в десятичные и, в зависимости от ограничений plarform, даже больше, она также предоставляет вам инструменты для безопасной проверки правильности строки, подлежащей преобразованию:

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

int main(void)
{
    errno = 0; // reset errno
    unsigned long long value;
    char *endptr;
    char str[] = "1111111111111111111111111111111111111111111111111111111111111111";

    value = strtoull(str, amp;endptr, 2);

    if(errno == ERANGE){ //value too large
        perror("Overflow");
        return EXIT_FAILURE;
    }

    if(*endptr != ''){ //if str has invalid characters
        puts("Invalid value");
        return EXIT_FAILURE;
    }

    printf("%llu", value);
    return EXIT_SUCCESS;
}
  

Вывод:

 18446744073709551615
  

Live demo

Комментарии:

1. здравствуйте, итак, код работает для большинства из них, кроме последнего, который равен 64 * 1. предполагается, что его вывод равен 18446744073709551615.

2. Чтобы получить это, @MohamedElMoursi, вам нужны соответствующие изменения для использования unsigned long long . Довольно много изменений.

3. @JonathanLeffler если все в порядке, могу ли я получить несколько советов, которые приведут меня в правильном направлении?

4. Когда это str[strlen(str)] не то же самое, что ’’ ?

5. @JonathanLeffler честно говоря, я очень новичок в кодировании, я понятия не имею