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

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

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

Смотрите ссылку

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

Изменить Я проверил C++11 и C11 Стандарты и вот что я нашел:

C++11, §18.4.1:

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

C11, §7.20.1.1:

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

3 ответа

Решение

все еще переполнение этих типов неопределенным поведением?

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

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

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

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

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

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

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

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

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

Этот ответ о новых разработках по этому вопросу - через 6 лет после того, как ОП задал свой вопрос (2013 -> 2019):

В C++20 целочисленное переполнение со знаком будет иметь определенное поведение.

Последнее предложение для этого изменения было этим. Группа по стандартизации ISO C++ собралась в Сан-Диего в конце 2018 года, и в отчете о поездке говорится, что формулировка для реализации этого предложения была выбрана для C++20.

Примечание. Тот факт, что за него проголосовали, не означает, что вы можете начать предполагать, что это определенное поведение. Только когда:

  1. C++20 опубликован
  2. Ваш компилятор заявляет о поддержке C++20 (или специально для этой функции)
  3. Вы говорите своему компилятору следовать стандарту C++20 (например, -std=c++20 или же -std=c++2a)

сможете ли вы положиться на это изменение? Через пару лет.

Я бы поспорил так.

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

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

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

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

Другие вопросы по тегам