Является ли подписанное целочисленное переполнение неопределенным поведением в C ++?


84

Как известно, целочисленное переполнение со знаком - это неопределенное поведение . Но в cstdintдокументации по C ++ 11 есть кое-что интересное :

целочисленный тип со знаком шириной ровно 8, 16, 32 и 64 бита соответственно без битов заполнения и с использованием дополнения до 2 для отрицательных значений (предоставляется только в том случае, если реализация напрямую поддерживает тип)

См. Ссылку

И вот мой вопрос: так как стандарт прямо говорит , что для int8_t, int16_t, int32_tиint64_t отрицательных чисел 2 - х комплемент, по - прежнему переполнение этих типов неопределенного поведения?

редактировать Я проверил стандарты C ++ 11 и C11, и вот что я нашел:

С ++ 11, §18.4.1:

Заголовок определяет все функции, типы и макросы так же, как 7.20 в стандарте C.

C11, §7.20.1.1:

Имя typedef intN_tобозначает целочисленный тип со знаком шириной N, без битов заполнения и представление с дополнением до двух. Таким образом, int8_tобозначает такой знаковый целочисленный тип шириной ровно 8 бит.


14
Никогда не забывайте, что единственной первичной документацией для C ++ является стандарт. Все остальное, даже такая вики, как CppReference, является вторичным источником. Это не значит, что это неправильно; просто не совсем надежно.
Никол Болас

Я ожидал, что это будет UB, для этих типов в C нет исключения, я не понимаю, почему C ++ добавил бы один.
Дэниел Фишер,


3
Я немного сбит с толку: где глагол в предложении "целочисленный тип со знаком шириной ровно 8, 16, 32 и 64 бит соответственно, без битов заполнения и с использованием дополнения 2 для отрицательных значений (при условии, только если реализация напрямую поддерживает тип)? " Немного не хватает? Что это означает?
YSC

C ++ 11 основан на C99, а не на C11. Но в любом случае это не важно
LF

Ответы:


81

по-прежнему ли переполнение этих типов неопределенным поведением?

Да. Согласно параграфу 5/4 стандарта C ++ 11 (в отношении любого выражения в целом):

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

Тот факт, что для этих типов со знаком используется представление с двумя дополнениями, не означает, что арифметика по модулю 2 ^ n используется при оценке выражений этих типов.

Что касается беззнаковой арифметики, с другой стороны, Стандарт явно указывает, что (Параграф 3.9.1 / 4):

Беззнаковых целых, объявили unsigned, должны подчиняться законам арифметики по модулю 2 ^ п , где п число битов в представлении значение этого конкретного размера целого числа

Это означает, что результат беззнаковой арифметической операции всегда " математически определен ", и результат всегда находится в пределах представимого диапазона; следовательно, 5/4 не применяется. Сноска 46 объясняет это:

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


1
Этот абзац также подразумевает, что беззнаковое переполнение не определено, а это не так.
Арчи

8
@Archie: Не совсем так, поскольку значения без знака определяются по модулю беззнакового диапазона.
Гонки легкости на орбите

3
@Archie: Я пытался уточнить, но в основном вы получили ответ от LightnessRacesinOrbit
Энди Проул

1
На самом деле не имеет значения, определено ли переполнение без знака или нет, если оно не может произойти из-за вычисления по модулю ...
Аконкагуа

1
Существуют беззнаковые операции, результат которых не «математически определен» - особенно деление на ноль - поэтому, возможно, ваша формулировка не совсем то, что вы имели в виду в этом предложении. ITYM: когда результат определяется математически , он также определяется в C ++.
Тоби Спейт

22

Просто потому, что тип определен для использования представления с двумя дополнениями, это не означает, что арифметическое переполнение в этом типе становится определенным.

Неопределенное поведение подписанного арифметического переполнения используется для оптимизации; например, компилятор может предположить, что если, a > bто a + 1 > bтакже; это не выполняется в беззнаковой арифметике, где вторая проверка должна быть выполнена из-за возможности, что она a + 1может завершиться 0. Кроме того, некоторые платформы могут генерировать сигнал прерывания при арифметическом переполнении (см., Например, http://www.gnu.org/software/libc/manual/html_node/Program-Error-Signals.html ); стандарт по-прежнему допускает это.


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

3
... рекомендуется, чтобы реализации были более конкретными, когда это возможно). На оборудовании, где числа в дополнительном коде до двух будут "обернуты" естественным образом, нет разумной причины для кода, который хочет получить обернутый целочисленный результат для выполнения многих инструкций, пытающихся выполнить без целочисленного переполнения вычисление, которое оборудование могло бы выполнить всего за одну или две инструкции .
supercat

1
@supercat Фактически, код, которому требуется обернутый результат, может (на двух дополнительных процессорах) просто приводить операнды к соответствующим беззнаковым типам и выполнять операцию (а затем отбрасывать назад, получая значение, определяемое реализацией): это работает для сложения, вычитания и умножения . Единственная проблема связана с делением, модулем и такими функциями, как abs. Для тех операций, когда он работает, не требуется больше инструкций, чем при подписанной артметике.
Руслан

@Ruslan: в тех случаях, когда коду требуется точно завернутый результат, приведение к беззнаковому было бы некрасиво, но не обязательно генерировало бы дополнительный код. Более серьезная проблема будет с кодом, который должен быстро идентифицировать «потенциально интересных» кандидатов, который будет проводить большую часть своего времени, отклоняя неинтересных кандидатов. Если кто-то дает компилятору свободу произвольно сохранять или отбрасывать дополнительную точность с целочисленными значениями со знаком, но требует, чтобы приведение назад к целочисленному типу усекало любую такую ​​точность, это обеспечит большую часть полезных оптимизаций, которые могут быть достигнуты путем переполнения UB , ...
supercat

... но позволит коду, которому требуется точная упаковка, использовать одно приведение, а не два (например, (int)(x+y)>zбудет сравнивать завернутый результат), а также позволит программистам писать x+y>zв тех случаях, когда было бы приемлемо для кода выдавать 0 или 1 в случае переполнения при условии, что у него нет других побочных эффектов . Если равным образом приемлемым результатом будет либо 0, либо 1, позволяя программисту писать это, а не любую из них, (long)x+y>zили (int)((unsigned)x+y)>zпозволит компиляторам выбрать ту из последних функций, которая будет дешевле в любом данном контексте [каждая из них будет дешевле в некоторых случаях].
supercat

1

Готов поспорить.

Из стандартной документации (стр.4 и 5):

1.3.24 неопределенное поведение

поведение, к которому данный международный стандарт не предъявляет требований

[Примечание. Неопределенное поведение может ожидаться, если в этом международном стандарте отсутствует какое-либо явное определение поведения или когда программа использует ошибочную конструкцию или ошибочные данные. Допустимое неопределенное поведение варьируется от полного игнорирования ситуации с непредсказуемыми результатами до поведения во время трансляции или выполнения программы задокументированным образом, характерным для среды (с выдачей диагностического сообщения или без него), до прекращения трансляции или выполнения (с выдачей диагностического сообщения). Многие ошибочные программные конструкции не приводят к неопределенному поведению; они должны быть диагностированы. - конец примечания]

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.