Разница в поведении целого числа без знака и со знаком, когда происходит переполнение целого числа

Читая эту статью в Википедии о Целочисленное переполнение

Я не совсем понимаю ту часть, почему переполнение целых чисел со знаком вызывает неопределенное поведение, но переполнение целых чисел без знака вызывает обтекание. Почему существует различие в их поведении?

Еще вопросы: есть ли вообще языки программирования для защиты от целочисленного переполнения?

4

Решение

Основное обоснование различия заключается в том, что спецификация языка C и C ++ позволяет реализации использовать одно из следующих трех различных целочисленных представлений со знаком.

  1. 1 дополнение
  2. 2-е дополнение
  3. Подписанная величина

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

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

Причиной поведения является не определено (в отличие от * unspecified «) потому, что существуют платформы, которые намеренно генерируют аппаратные исключения в случае переполнения со знаком во время целочисленной арифметики. Обратите внимание, что поведение не определено только для арифметических операций, которые обычно выполняются машиной. Преобразование значений со знаком переполнения не приводит к неопределенному поведению (поведение фактически определяется реализацией).

Что касается неподписанных типов, они представлены одинаково на всех платформах, что означает, что требование согласованного поведения на всех платформах не является проблемой. Обтекание, которое концептуально соответствует поведению «по модулю 2 ^ ширина», является естественным поведением практически на всех известных бинарных аппаратных платформах.

2

Другие решения

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

  2. Зависит от языка. Немного аппаратные средства и вы можете воспользоваться этим в своей программе.

1

Методология C / C ++ для целочисленного переполнения состоит в том, чтобы обеспечить наиболее быстрое поведение на машине, на которой вы работаете, и на некоторых машинах (здесь предполагается использование 16-разрядных целых чисел со знаком):

32766 + 2 == -32768

но на некоторых машинах есть:

32766 + 2 == 32767

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

Примечание. Java имеет целочисленное переполнение для достижения цели: «Пиши один раз, беги везде».

Что касается целых чисел без знака — большинство их приложений — битовые маски, битовые поля и манипуляции с числами (операции по модулю, идентификаторы) — именно те операции, для которых вы не хотите, чтобы они были неопределенными.

Некоторые из языков программирования имеют такие меры безопасности, некоторые нет:

  • Python 3 автоматически преобразует значения, которые переполняются, чтобы иметь тип long (произвольно большие целые числа).

  • В C / C ++ вы должны сами проверить условия переполнения, заголовки climits (C) и limit (C ++) определили максимальные и минимальные значения для каждого типа.

  • Программирование в сборке x86 — есть CF (флаг переноса) для неподписанного и OF (флаг переполнения) для подписанного в регистрах FLAGS и EFLAGS, чтобы проверить, когда происходит переполнение.

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

1

В Java вы только подписали int а также long значения и их поведение является последовательным, где бы вы ни запускали. Если вы добавляете 1 к Integer.MAX_VALUE, вы делаете ставку на Integer.MIN_VALUE (он переносится), а если вычитаете 1 из Long.MIN_VALUE, вы получаете Long.MAX_VALUE.

Поэтому я понятия не имею, почему поведение беззнакового значения будет неопределенным в других языках.

0
По вопросам рекламы [email protected]