Когда RHS имеют отрицательные значения int и unsigned int вне диапазона значений int в арифметической операции - PullRequest
0 голосов
/ 02 июня 2018

Я прошу прощения за заголовок, так как мне пришлось каким-то образом найти уникальный.

Рассмотрим код ниже:

#include<stdio.h>
int main(void)
{
  int b = 2147483648; // To show the maximum value of int type here is 2147483647
  printf("%d\n",b);

  unsigned int a = 2147483650;
  unsigned int c = a+(-1);
  printf("%u\n",c);
}

Вывод вышеуказанной программы при запуске на 64-битнойОперационная система с компилятором gcc:

-2147483648
 2147483649

Пожалуйста, ознакомьтесь с моим пониманием случая:

Без знака int a находится вне диапазона со знаком типа int.В RHS (-1) будет преобразовано в unsigned int, так как операнды бывают разных типов.Результат преобразования -1 в целое число без знака:

-1 + (unsigned int MAX_UINT +1) = unsigned int MAX_UINT = 4294967295.

Теперь RHS будет:

unsigned int MAX_UINT + 2147483650

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

Пожалуйста, дайте правильное объяснение.

PS: Знатькак int b = 2147483648 стало -2147483648 не мое намерение.Я только добавил эту строку в код, чтобы было совершенно ясно, что 2147483650 находится вне диапазона int.

Ответы [ 3 ]

0 голосов
/ 02 июня 2018

Посмотрите на это так

 2147483650  0x80000002
+        -1 +0xFFFFFFFF
 ----------  ----------
 2147483649  0x80000001

Откуда взялся 0xFFFFFFFF?Ну, 0 - это 0x00000000, и если вы вычтете 1 из этого, вы получите 0xFFFFFFFF, потому что арифметика без знака четко определена как "обтекание".

Или перевод вашей десятичной версии дальше, 0 - 1 равно UINT_MAX, потому что unsigned int оборачивается, как и сумма.

your value      2147483650
UINT_MAX      + 4294967295
                ----------
                6442450945
modulo 2^32   % 4294967296
                ----------
                2147483649
0 голосов
/ 02 июня 2018
int b = 2147483648;
printf("%d\n",b);
// -2147483648

Преобразование целого числа (любого со знаком или без знака), выходящего за пределы диапазона цели со знаком Тип:

..либо результат определяется реализацией, либо определяется сигнал реализации.C11 §6.3.1.3 3

В вашем случае с целым числом со знаком 2147483648 определенное реализацией поведение отображает младшие 32 бита источника 2147483648 в ваши int32 бита.Это может не быть результатом с другим компилятором.


a+(-1) - это то же самое, что a + (-(1u)), то же самое, что a + (-1u + UINT_MAX + 1u), что и a + UINT_MAX. добавление переполняет диапазон unsigned, но переполнение unsigned оборачивается.Таким образом, сумма составляет 2147483649 до назначения.С приведенным ниже кодом, нет преобразования вне диапазона.Единственное преобразование - signed 1 в unsigned 1 и long 2147483650 (или long long 2147483650) в unsigned 2147483650.Оба в диапазонах преобразования.

unsigned int a = 2147483650;
unsigned int c = a+(-1);
printf("%u\n",c);
//  2147483649
0 голосов
/ 02 июня 2018

2147483648 не является 32-битным int, оно чуть выше INT_MAX, значение которого на таких платформах равно 2147483647.

int b = 2147483648; определяется реализацией.На вашей платформе он, похоже, выполняет 32-разрядное преобразование, что типично для двух дополнительных архитектур, но не гарантируется стандартом C.

Как следствие printf("%d\n", b); вывод -2147483648.

Остальная часть кода отлично определена в 32-разрядных системах, и вывод 2147483649 является правильным и ожидаемым.Тот факт, что ОС на 64 бит играет очень тонкую роль на этапах оценки, но в основном не имеет отношения к фактическому результату, который полностью определяется стандартом C.

Вот шаги:

  • unsigned int a = 2147483650; здесь нет ничего удивительного, a - это unsigned int, а его инициализатор - int, long int или long long int, в зависимости от того, какойиз этих типов имеет не менее 32 битов значения.В Windows и 32-битной Linux это будет long long int, тогда как в 64-битной Linux это будет long int.Значение сохраняется до 32-битного при сохранении в переменную unsigned int.

    Вы можете проверить эти шаги, добавив следующий код:

    printf("sizeof(2147483650) -> %d\n", (int)sizeof(2147483650));
    printf("         sizeof(a) -> %d\n", (int)sizeof(a));
    
  • ВторойОпределение unsigned int c = a+(-1); подвергается тем же шагам:

    • c определяется как unsigned int, и его инициализатор усекается до 32 бит при сохранении в c.Инициализатор является дополнением:
    • первый член - unsigned int со значением 2147483650U.
    • второй член - это выражение в скобках с унарным отрицанием int со значением1.Следовательно, это int со значением -1, как вы правильно проанализировали.
    • второй член преобразуется в unsigned int: преобразование выполняется по модулю 2 32 , следовательно, значение4294967295U.
    • затем выполняется сложение с использованием арифметики без знака, которая определяется как выполняемая по модулю ширины типа unsigned int, следовательно, результатом является unsigned int со значением 2147483649U, (6442450945 по модулю 2 32 )
    • Это значение unsigned int сохраняется в c и печатается правильно с printf("%u\n", c); как 2147483649.

Если бы выражение было вместо 2147483650 + (-1), вычисление было бы выполнено в 64-битной арифметике со знаком, с типом long int или long long int в зависимости от архитектуры, с результатом 2147483649,Это значение затем будет усечено до 32 бит при сохранении в c, следовательно, то же значение для c, что и 2147483649.

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

Вы можете проверить эти шаги с помощью дополнительного кода.Вот полная инструментальная программа, иллюстрирующая эти шаги:

#include <limits.h>
#include <stdio.h>

int main(void) {
    printf("\n");
    printf("             sizeof(int) -> %d\n", (int)sizeof(int));
    printf("    sizeof(unsigned int) -> %d\n", (int)sizeof(unsigned int));
    printf("        sizeof(long int) -> %d\n", (int)sizeof(long int));
    printf("   sizeof(long long int) -> %d\n", (int)sizeof(long long int));
    printf("\n");

    int b = 2147483647; // To show the maximum value of int type here is 2147483647
    printf("                   int b =  2147483647;\n");
    printf("                       b -> %d\n",b);
    printf("               sizeof(b) -> %d\n", (int)sizeof(b));
    printf("      sizeof(2147483647) -> %d\n", (int)sizeof(2147483647));
    printf("      sizeof(2147483648) -> %d\n", (int)sizeof(2147483648));
    printf("     sizeof(2147483648U) -> %d\n", (int)sizeof(2147483648U));
    printf("\n");

    unsigned int a = 2147483650;
    printf("          unsigned int a =  2147483650;\n");
    printf("                       a -> %u\n", a);
    printf("     sizeof(2147483650U) -> %d\n", (int)sizeof(2147483650U));
    printf("      sizeof(2147483650) -> %d\n", (int)sizeof(2147483650));
    printf("\n");

    unsigned int c = a+(-1);
    printf("          unsigned int c =  a+(-1);\n");
    printf("                       c -> %u\n", c);
    printf("               sizeof(c) -> %d\n", (int)sizeof(c));
    printf("                  a+(-1) -> %u\n", a+(-1));
    printf("          sizeof(a+(-1)) -> %d\n", (int)sizeof(a+(-1)));
#if LONG_MAX == 2147483647
    printf("         2147483650+(-1) -> %lld\n", 2147483650+(-1));
#else
    printf("         2147483650+(-1) -> %ld\n", 2147483650+(-1));
#endif
    printf(" sizeof(2147483650+(-1)) -> %d\n", (int)sizeof(2147483650+(-1)));
    printf("        2147483650U+(-1) -> %u\n", 2147483650U+(-1));
    printf("sizeof(2147483650U+(-1)) -> %d\n", (int)sizeof(2147483650U+(-1)));
    printf("\n");

    return 0;
}

Вывод:

             sizeof(int) -> 4
    sizeof(unsigned int) -> 4
        sizeof(long int) -> 8
   sizeof(long long int) -> 8

                   int b =  2147483647;
                       b -> 2147483647
               sizeof(b) -> 4
      sizeof(2147483647) -> 4
      sizeof(2147483648) -> 8
     sizeof(2147483648U) -> 4

          unsigned int a =  2147483650;
                       a -> 2147483650
     sizeof(2147483650U) -> 4
      sizeof(2147483650) -> 8

          unsigned int c =  a+(-1);
                       c -> 2147483649
               sizeof(c) -> 4
                  a+(-1) -> 2147483649
          sizeof(a+(-1)) -> 4
         2147483650+(-1) -> 2147483649
 sizeof(2147483650+(-1)) -> 8
        2147483650U+(-1) -> 2147483649
sizeof(2147483650U+(-1)) -> 4
...