Почему 0 <-0x80000000?

У меня есть простая программа:

#include <stdio.h>

#define INT32_MIN        (-0x80000000)

int main(void) 
{
    long long bal = 0;

    if(bal < INT32_MIN )
    {
        printf("Failed!!!");
    }
    else
    {
        printf("Success!!!");
    }
    return 0;
}

Условие if(bal < INT32_MIN ) всегда верно. Как это возможно?

Он отлично работает, если я изменил макрос на:

#define INT32_MIN        (-2147483648L)

Может ли кто-нибудь указать на проблему?

Ответ 1

Это довольно тонко.

Каждый целочисленный литерал в вашей программе имеет тип. Какой тип, который он имеет, регулируется таблицей в 6.4.4.1:

Suffix      Decimal Constant    Octal or Hexadecimal Constant

none        int                 int
            long int            unsigned int
            long long int       long int
                                unsigned long int
                                long long int
                                unsigned long long int

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

  • Попробуйте int
  • Если он не подходит, попробуйте long
  • Если он не подходит, попробуйте long long.

Шестнадцатеричные литералы ведут себя по-разному! Если литерал не может вписываться в подписанный тип типа int, сначала попробуйте unsigned int, прежде чем переходить к более сложным типам. См. Разницу в приведенной выше таблице.

Итак, в 32-битной системе ваш литерал 0x80000000 имеет тип unsigned int.

Это означает, что вы можете применить унарный оператор - в литерале, не вызывая поведение, определяемое реализацией, как это было бы иначе при переполнении целого числа со знаком. Вместо этого вы получите значение 0x80000000, положительное значение.

bal < INT32_MIN вызывает обычные арифметические преобразования, а результат выражения 0x80000000 продвигается от unsigned int до long long. Значение 0x80000000 сохраняется и 0 меньше 0x80000000, следовательно результат.

Когда вы заменяете литерал 2147483648L, вы используете десятичную нотацию, и поэтому компилятор не выбирает unsigned int, а пытается поместить его внутри long. Также суффикс L говорит, что вы хотите long, если это возможно. Суффикс L фактически имеет схожие правила, если вы продолжаете читать указанную таблицу в 6.4.4.1: если номер не помещается внутри запрошенного long, чего нет в 32-битном случае, компилятор даст вам a long long, где он будет соответствовать только штрафу.

Ответ 2

0x80000000 - это литерал unsigned со значением 2147483648.

Применение унарного минуса на этом по-прежнему дает вам неподписанный тип с ненулевым значением. (Фактически, для ненулевого значения x значение, в котором вы закончили, равно UINT_MAX - x + 1.)

Ответ 3

Этот целочисленный литерал 0x80000000 имеет тип unsigned int.

В соответствии со стандартом C (6.4.4.1 Целочисленные константы)

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

И эта целочисленная константа может быть представлена ​​типом unsigned int.

Итак, это выражение

-0x80000000 имеет тот же тип unsigned int. Кроме того, он имеет такое же значение 0x80000000 в двух дополнительных представлениях, которые вычисляют следующий путь

-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000

Это имеет побочный эффект, если написать, например,

int x = INT_MIN;
x = abs( x );

Результат будет снова INT_MIN.

Таким образом, в этом условии

bal < INT32_MIN

сравнивается 0 с unsigned value 0x80000000, преобразованный в тип long long int в соответствии с правилами обычных арифметических преобразований.

Очевидно, что 0 меньше 0x80000000.

Ответ 4

Числовая константа 0x80000000 имеет тип unsigned int. Если взять -0x80000000 и сделать 2-х комплиментную математику на нем, мы получим следующее:

~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000

Итак -0x80000000 == 0x80000000. И сравнение (0 < 0x80000000) (так как 0x80000000 без знака) истинно.

Ответ 5

Точка путаницы возникает при мысли, что - является частью числовой константы.

В приведенном ниже коде 0x80000000 есть числовая константа. Его тип определяется только этим. - применяется после и не меняет тип.

#define INT32_MIN        (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )

Необработанные числовые константы положительны.

Если он десятичен, то назначенный тип является первым типом, который будет удерживать его: int, long, long long.

Если константа восьмеричная или шестнадцатеричная, она получает первый тип, который ее поддерживает: int, unsigned, long, unsigned long, long long, unsigned long long.

0x80000000, на OP система получает тип unsigned или unsigned long. В любом случае, это какой-то неподписанный тип.

-0x80000000 также является некоторым ненулевым значением и является некоторым неподписанным типом, оно больше 0. Когда код сравнивает это с long long, значения не изменяются на 2 сторонах сравнения, поэтому 0 < INT32_MIN истинно.


Альтернативное определение избегает этого любопытного поведения

#define INT32_MIN        (-2147483647 - 1)

Пойдем в фантастическую землю на некоторое время, когда int и unsigned являются 48-битными.

Тогда 0x80000000 вписывается в int, а также тип int. -0x80000000 - это отрицательное число, и результат распечатки отличается.

[Вернуться к реальному слову]

Так как 0x80000000 подходит к некоторому неподписанному типу перед подписанным типом, так как он больше, чем some_signed_MAX, но внутри some_unsigned_MAX, это какой-то неподписанный тип.

Ответ 6

C имеет правило, что целочисленный литерал может быть signed или unsigned зависит от того, подходит ли он в signed или unsigned (целая продвижение). На 32 -битовой машине буква 0x80000000 будет unsigned. 2 дополнения -0x80000000 составляет 0x80000000 на 32-битной машине. Поэтому сравнение bal < INT32_MIN находится между signed и unsigned, и перед сравнением в соответствии с правилом C unsigned int будет преобразовано в long long.

C11: 6.3.1.8/1:

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

Следовательно, bal < INT32_MIN всегда true.