Почему беззнаковые "маленькие" целые числа продвигаются к подписанному int? - PullRequest
6 голосов
/ 14 июня 2019

Стандарт понятен: при выполнении арифметики с целочисленным типом, меньшим int, целое число сначала переводится в int со знаком, если только int не может представлять полный диапазон значений для исходного типа, в в этом случае вместо этого действует unsigned int.

Мой вопрос: что является (было?) Мотивацией для этой политики? Почему неподписанные типы повышаются до int, а не всегда до unsigned int?

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

Были ли исторические причины для предпочтения подписи int? Существуют ли архитектуры, в которых не используется арифметика с дополнением до двух, в которой продвижение небольших типов без знака на int вместо unsigned int проще / быстрее?

РЕДАКТИРОВАТЬ: Я думаю, что это очевидно, но здесь я ищу факты (то есть некоторую документацию или ссылки, которые объясняют проектное решение), а не "в основном основанные на мнении" спекуляции.

Ответы [ 2 ]

10 голосов
/ 14 июня 2019

Об этом говорится в Обоснование ANSI C (ссылка на соответствующий раздел, 3.2.1.1). В какой-то степени это был произвольный выбор, который мог пойти в любом случае, но есть причины для такого выбора.

Со времени публикации K & R произошла серьезная разница реализации C в эволюции интегральных правил продвижения. Реализации делятся на два основных лагеря, которые можно охарактеризовать как беззнаковое сохранение и сохранение значения . Различия между этими подходами центры по лечению unsigned char и unsigned short при расширении с помощью интегральных повышений 1013 *, но решение также влияет на типизацию констант (см. §3.1.3.2).

Подход без знака требует продвижения двух меньших беззнаковые типы unsigned int. Это простое правило и дает тип, который не зависит от среды исполнения.

Подход с сохранением значения требует продвижения этих типов в signed int, если этот тип может правильно представлять все значения оригинальный тип, и в противном случае для продвижения этих типов в unsigned int. Таким образом, если среда выполнения представляет short как нечто меньшее, чем int, unsigned short становится int; в противном случае оно становится unsigned int.

[СНИП]

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

(Я рекомендую прочитать полный раздел. Я просто не хотел цитировать все это здесь.)

0 голосов
/ 17 июля 2019

Интересная часть Обоснования взята из ответа Кейта Томпсона:

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

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

  2. Результат предыдущего выражения используется в контексте, в котором его подпись значима:

    • sizeof (int)
    • это левый операнд оператора правого смещения (в реализации, где это смещение определено как арифметическое), или
    • это либо операнд /,%, <, <=,>, либо> =.

Обратите внимание, что Стандарт не предъявляет никаких требований к тому, как реализация обрабатывает любую ситуацию, в которой было бы уместным поведение с обходным циклом.Явное следствие заключается в том, что авторы Стандарта ожидали, что обычные реализации для платформ с двумя дополнительными компонентами будут вести себя так, как описано выше, с мандатом или без него, при отсутствии веской причины делать иное, и, следовательно, не было необходимости предписывать, чтобы они это делали.,Хотя кажется маловероятным, что они рассмотрели возможность того, что 32-битная реализация, имеющая что-то вроде:

unsigned mul(unsigned short x, unsigned short y) { return x*y; }

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

...