Почему неподписанное целочисленное переполнение определенного поведения, но недопустимое целочисленное число целых чисел? - программирование
Подтвердить что ты не робот

Почему неподписанное целочисленное переполнение определенного поведения, но недопустимое целочисленное число целых чисел?

Беззнаковое целочисленное переполнение хорошо определено как стандартами C, так и С++. Например, C99 standard (§6.2.5/9) указывает

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

Однако, оба стандарта заявляют, что подписанное целочисленное переполнение - это поведение undefined. Опять же, из стандарта C99 (§3.4.3/1)

Примером неустановленного поведения является поведение по целому числу по потоку

Есть ли историческая или (даже лучше!) техническая причина этого несоответствия?

4b9b3361

Ответ 1

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

На практике только представления для подписанных значений могут различаться в зависимости от реализации: одно дополнение, два дополнения, знак-величина. Для неподписанного типа нет причины, чтобы стандарт допускал вариацию, потому что существует только одно очевидное двоичное представление (стандарт допускает только двоичное представление).

Релевантные кавычки:

C99 6.2.6.1:3:

Значения, хранящиеся в неподписанных битовых полях и объектах типа unsigned char, должны быть представлены с использованием чистой двоичной нотации.

C99 6.2.6.2:2:

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

- соответствующее значение со знаком бит 0 отрицается (знак и величина);

- бит знака имеет значение - (2 N) (два дополнения);

- знаковый бит имеет значение - (2 N - 1) (одно дополнение).


В настоящее время все процессоры используют два дополнительных представления, но подписанное арифметическое переполнение остается undefined, а разработчики компиляторов хотят, чтобы он оставался undefined, потому что они используют эту неопределенность для оптимизации. См., Например, этот сообщение в блоге Яна Лэнса Тейлора или жалобы от Agner Fog и ответы на его отчет об ошибке.

Ответ 2

Помимо хорошего ответа Pascal (который, я уверен, является основной мотивацией), также возможно, что некоторые процессоры вызывают исключение из целочисленного переполнения со знаком, что, конечно, вызовет проблемы, если компилятор должен был "организовать другой поведение" (например, используйте дополнительные инструкции для проверки потенциального переполнения и вычисления в этом случае по-разному).

Также стоит отметить, что "поведение undefined" не означает, что "не работает". Это означает, что реализации разрешено делать то, что ему нравится в этой ситуации. Это включает в себя выполнение "правильной вещи", а также "вызов полиции" или "сбой". Большинство компиляторов, когда это возможно, будут выбирать "делать правильные вещи", считая, что относительно легко определить (в данном случае это так). Однако, если у вас возникают переполнения в вычислениях, важно понять, что это на самом деле приводит, и что компилятор МОЖЕТ делать что-то другое, кроме того, что вы ожидаете (и это может сильно зависеть от версии компилятора, настроек оптимизации и т.д.),

Ответ 3

В дополнение к другим упомянутым вопросам, если беззнаковый математический перенос приводит к тому, что целые типы без знака ведут себя как абстрактные алгебраические группы (что означает, среди прочего, для любой пары значений X и Y, будут существовать некоторые другое значение Z такое, что X+Z будет, если правильно лить, равно Y и Y-Z будет, если правильно выбрано, равно X). Если значения без знака были просто типами расположения хранилища, а не типами промежуточного выражения (например, если не было беззнакового эквивалента самого большого целочисленного типа, а арифметические операции с неподписанными типами вели себя так, как если бы они сначала преобразовали их в более крупные типы подписей, тогда было бы не так много потребности в определенном оберточном поведении, но было бы сложно делать вычисления в типе, который не имеет, например, аддитивного обратного.

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

Ответ 4

Прежде всего, обратите внимание, что C11 3.4.3, как и все примеры и ноты, не является нормативным текстом и поэтому не имеет отношения к цитированию!

Соответствующий текст, который гласит, что переполнение целых чисел и чисел с плавающей запятой - это поведение undefined:

C11 6.5/5

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

Подробное описание поведения целых чисел без знака можно найти здесь:

C11 6.2.5/9

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

Это делает неподписанные целые типы особым случаем.

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

C11 6.3.1.3

6.3.1.3 Целочисленные и беззнаковые целые числа

Когда значение с целым числом type преобразуется в другой целочисленный тип, отличный от _Bool, если значение может быть представлено новым типом, оно не изменяется.

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

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

Ответ 5

Возможно, еще одна причина того, почему unsigned арифметика определена, состоит в том, что беззнаковые числа образуют целые числа по модулю 2 ^ n, где n - ширина беззнакового числа. Беззнаковые числа представляют собой просто целые числа, представленные двоичными цифрами вместо десятичных цифр. Выполнение стандартных операций в системе модулей хорошо понято.

Цитата OP ссылается на этот факт, но также подчеркивает тот факт, что существует только один, недвусмысленный, логичный способ представления целых чисел без знака в двоичном формате. Напротив, Подписанные числа чаще всего представлены с использованием двух дополнений, но возможны другие варианты, как описано в стандарте (раздел 6.2.6.2).

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