Разветвления C ++ 20, требующие дополнения до двух

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

Но мне было интересно, может ли это изменение привести к тому, что некоторые «неопределенные поведения» станут «определенными реализацией» или даже «определенными».

Рассмотрим функцию абсолютного значения std::abs(int) и некоторые из ее перегрузок. Стандарт C ++ включает эту функцию со ссылкой на стандарт C, в котором говорится, что поведение не определено, если результат не может быть представлен.

В дополнении до двух нет положительного аналога INT_MIN:

abs(INT_MIN) == -INT_MIN == undefined behavior

В знаково-величинном представлении есть:

-INT_MIN == INT_MAX

Таким образом, казалось разумным, что abs() остался с каким-то неопределенным поведением.

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

Единственный недостаток, который я вижу, заключается в том, что в стандарте C ++ необходимо явно указать abs(), а не ссылаться на описание abs() в стандарте C. (Насколько мне известно, C не требует дополнения до двух.)

Разве это просто не было приоритетом для комитета или все еще есть причины не использовать упрощение и уверенность, которые дает мандат на дополнение двух?


person Adrian McCarthy    schedule 05.08.2019    source источник


Ответы (2)


Один из конкретных вопросов, рассмотренных комитету было что делать с -INT_MIN, и результаты этого опроса были:

сложение / вычитание / умножение и -INT_MIN переполнение в настоящее время является неопределенным поведением, вместо этого оно должно быть:

4: перенос
6: перенос или ловушка
5: промежуточные значения - математические целые числа
14: статус-кво (поведение остается неопределенным)

Это было явно рассмотрено, и люди посчитали, что лучший вариант - сохранить неопределенное поведение.

Чтобы уточнить «промежуточные значения - математические целые числа», есть другая часть статьи, в которой поясняется, что это означает, что (int)a + (int)b > INT_MAX может быть правдой.


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

person Barry    schedule 05.08.2019
comment
Просто любопытно, кто-нибудь когда-либо предлагал установить INT_MIN на -2147483647 и рассматривать значение -2147483648 как NaN? - person Bob__; 05.08.2019
comment
@Bob__ Не то, чтобы я в курсе. - person Barry; 05.08.2019
comment
@Bob__: Этот подход был бы полезен, если бы Стандарт позволял LLONG_MIN быть немного выше, чем -LLONG_MAX. Обработка -INT_MAX-1 как NaN (и аналогично с другими целочисленными типами) была бы семантически полезной, но несколько дорогой без аппаратной поддержки, а аппаратная поддержка на платформах, размер регистров которых меньше long long, была бы практичной только при наличии зарезервированного верхнего -слово значение для NaN. - person supercat; 06.08.2019
comment
На вопрос, который вы процитировали, не было ли определенного варианта реализации? Будет ли требование реализации для выбора и документирования того, как они работают, -INT_MIN уменьшит возможности оптимизации? Похоже, что если бы они были готовы рассмотреть возможность его определения, то разве определение реализации не было бы разумным вариантом? - person Adrian McCarthy; 06.08.2019
comment
@AdrianMcCarthy Я думаю, что никто не хотел, чтобы реализации в этом случае имели другое определенное поведение. - person Barry; 06.08.2019
comment
@AdrianMcCarthy: Также не было возможности разрешить реализации - на досуге - выполнять вычисления с неуказанным большим типом (аналогично вычислениям с плавающей запятой). Правило, которое позволяет программистам игнорировать переполнение в случаях, когда такое поведение отвечает требованиям, допускает более полезные оптимизации, чем та, которая требует от программистов избегать переполнения любой ценой. - person supercat; 06.08.2019

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

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

В конечном итоге что-то вроде: unsigned mul_mod_65536(unsigned short x, unsigned short y) { return (x*y) & 0xFFFFu; } может произвольно нарушить поведение вызывающего кода, если арифметическое значение x*y находится между INT_MAX+1u и UINT_MAX, даже если это была бы ситуация, в которой, по словам авторов Стандарта, они ожидали, что большинство реализации. Недавний Стандарт устранил основную причину, по которой авторы C89 ожидали, что некоторые реализации могут странно обрабатывать вышеупомянутую функцию, но это не значит, что реализации не решили относиться к ней странным образом так, как авторы C89 никогда не могли вообразил, и никогда сознательно не позволил бы.

person supercat    schedule 05.08.2019