Второе дополнение к дополнению в C

Я видел багги код в C, который использовался для проверки того, добавляет ли результат переполнение или нет. Он отлично работает с char , но дает неправильный ответ, когда аргументы являются int и я не мог понять, почему.
Вот код с short аргументами.

 short add_ok( short x, short y ){ short sum = x+y; return (sum-x==y) && (sum-y==x); } 

Эта версия работает нормально, проблема возникает при изменении аргументов в int (вы можете проверить его с помощью INT_MAX )
Вы видите, что здесь не так?

Поскольку в дополнении 2s целые числа могут быть расположены в круг (в смысле модульной арифметики ). Добавление y, а затем вычитание y всегда возвращает вас туда, где вы начали (несмотря на неопределенное поведение).

В вашем коде добавление не переполняется, если int имеет такой же размер, как short . Из-за рекламных акций по умолчанию x+y выполняется для значений x и y направленных на int , а затем результат усекается до short в соответствии с реализацией.

Почему бы просто не сделать: return x+y<=SHRT_MAX && x+y>=SHRT_MIN;

В языке программирования C целые числа со знаком при преобразовании в меньшие значащие целые числа, например char (для простоты), имеют определенную реализацию. Несмотря на то, что многие системы и программисты предполагают переполнение оболочки, это не стандарт. Итак, что такое переполнение?

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

В подписанном символе наибольшее значение может быть представлено 127, а самое низкое – -128. Тогда что происходит, когда мы делаем: «char i = 128», является то, что значение, хранящееся в i, становится -128. Поскольку значение было больше, чем тип подписанного интеграла, он обернулся вокруг самого низкого значения, и если он был «char i = 129», тогда я буду содержать -127. Видишь? Всякий раз, когда конец достигает своего максимума, он обтекает другой конец (знак). И наоборот, если «char i = -129», тогда я буду содержать 127, а если это «char i = -130», он будет содержать 126, поскольку он достиг своего максимума и обернут вокруг самого высокого значения.

(самый высокий) 127, 126, 125, …, -126, -127, -128 (самый низкий)

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

точка обертывания для типа char


ОБНОВЛЕНИЕ: причина, по которой int не работает в противопоставлении char и short заключается в том, что при добавлении обоих чисел существует возможность переполнения (независимо от того, является ли int , short или char , а не забывает интегральную раскрутку), а потому "short" и char имеют меньшие размеры, чем int и поскольку они продвигаются до int в выражениях, они снова отображаются без усечения в этой строке:

 return (sum-x==y) && (sum-y==x); 

Таким образом, любое переполнение обнаруживается, как объясняется ниже, но когда с int он не продвигается ни на что, то происходит переполнение. Например, если я делаю INT_MAX+1 , то результат будет INT_MIN, и если я проверил на переполнение INT_MIN-1 == INT_MAX, результат будет TRUE! Это связано с тем, что «short» и char получают до int, оцениваются и затем усекаются (переполняются). Тем не менее, int сначала переполняется, а затем оценивается, потому что их не повышают до большего размера.

Подумайте о типе char без продвижения по службе и попробуйте сделать переполнение и проверить их, используя приведенную выше иллюстрацию. Вы обнаружите, что добавление или вычитание значений, вызывающих переполнение, возвращает вас туда, где вы были. Однако это не то, что происходит в C, потому что char и «short» продвигаются до int, поэтому обнаруживается переполнение, что неверно в int, потому что это примечание, увеличенное до большего размера.

КОНЕЦ ОБНОВЛЕНИЯ


На ваш вопрос, я проверил ваш код в MinGW и Ubuntu 12.04, кажется, работает нормально. Позднее я обнаружил, что код действительно работает в системах, где short меньше, чем int, и когда значения не превышают диапазон int. Эта строка:

 return (sum-x==y) && (sum-y==x); 

истинно, потому что «sum-x» и «y» оцениваются как (int), поэтому не происходит обертывания, где это произошло в предыдущей строке (при назначении):

 short sum = x+y; 

Вот тест. Если я ввел 32767 для первого и 2 для второго, тогда, когда:

 short sum = x+y; 

сумма будет содержать -32767, из-за обертки. Однако, когда:

 return (sum-x==y) && (sum-y==x); 

«sum-x» (-32767 – 32767) будет равняться y (2) (тогда багги), если произойдет обход-раунд, но из-за цельной рекламы это никогда не произойдет, и значение «sum-x» станет – 65534, который не равен y, что приводит к правильному обнаружению.

Вот код, который я использовал:

 #include  short add_ok( short x, short y ){ short sum = x+y; return (sum-x==y) && (sum-y==x); } int main(void) { short i, ii; scanf("%hd %hd", &i, &ii); getchar(); printf("%hd", add_ok(i, ii)); return 0; } 

Проверьте здесь и здесь .

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

Ссылка: C99 6.3.1.3 здесь и руководство GNU C здесь .

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

 return (y==y) && (x==x); 

а потом:

 return 1 

Это верно в каждом случае, потому что подписанное целочисленное переполнение является неопределенным поведением – следовательно, компилятор может гарантировать, что x + yy == x и y + xx == y.

Если это была неподписанная операция, она потерпела бы неудачу аналогично, поскольку переполнение просто выполняется как операция по модулю, довольно просто доказать, что

 x+y mod SHRT_MAX - y mod SHRT_MAX == x 

и аналогично для обратного случая.