Определяет ли С++ 14 поведение побитовых операторов для битов заполнения беззнакового целого числа?

стандарт С++

Если реализация C++14 включает биты заполнения в базовых байтах unsigned int , указывает ли стандарт, что побитовые операции не должны выполняться с битами заполнения?

Кроме того, указывает ли стандарт С++ 14, должны ли операторы равенства и отношения игнорировать биты заполнения?

Методические рекомендации

Если в этом вопросе отсутствует спецификация, существует ли какой-то консенсус в отношении ожидаемого поведения этих операторов при заполнении битов?

Я нашел противоречивые ответы на Stack Overflow. Легкие гонки на орбите и ecatmur говорят, что побитовые операторы не подходят для арифметики, потому что они применяются ко всем битам (включая биты заполнения), в то время как Кристоф и Бартек Банашевич говорят, что побитовые операторы работают с логическим значением целых чисел и игнорировать заполнение.

использованная литература

Связанные ответы: о наличии битов заполнения (1, 2, 3), в связи с отсутствием четкой спецификации C++ (4).

Определение битов заполнения в C++14 - § 3.9.1 - Основные типы:

Для узких типов символов все биты представления объекта участвуют в представлении значения. Для узких типов символов без знака все возможные битовые комбинации представления значения представляют числа. Эти требования не распространяются на другие типы.

Определение представления объекта и представления значения в C++14 — § 3.9 — Типы:

Объектное представление объекта типа T — это последовательность из N unsigned char объектов, занимаемых объектом типа T, где N равно sizeof(T) . Представление значения объекта — это набор битов, которые содержат значение типа T. Для тривиально копируемых типов представление значения представляет собой набор битов в представлении объекта, который определяет значение, которое является одним дискретным элементом набора значений, определяемого реализацией.44

Сноска 44) Цель состоит в том, чтобы модель памяти C++ была совместима с моделью памяти языка C ISO/IEC 9899.

Определение побитового И в С++ 14 - § 5.11 - Побитовый оператор И:

Выполняются обычные арифметические преобразования; результатом является побитовая функция И операндов. Оператор применяется только к целочисленным операндам или операндам перечисления с незаданной областью.

Определение сложения в C++14 — § 5.7 — Аддитивные операторы:

Обычные арифметические преобразования выполняются для операндов арифметического или перечислительного типа. Кроме того, [...] оба операнда должны иметь арифметический тип или тип перечисления с незаданной областью [...]. Результатом бинарного оператора + является сумма операндов.


person RalphS    schedule 18.01.2018    source источник
comment
Как ты мог сказать? Единственное, что вы можете получить, это значение, которое исключает биты заполнения.   -  person user207421    schedule 19.01.2018
comment
@EJP Проверяя представление объекта?   -  person T.C.    schedule 19.01.2018
comment
Побитовые операции определяются в терминах битов значения. У них вообще нет доступа к битам заполнения (если только вы не интерпретируете память как массив символов без знака).   -  person AnT    schedule 19.01.2018
comment
Связанный Каков результат a & b? говорит о некоторых недостатках в побитовых операциях.   -  person Shafik Yaghmour    schedule 19.01.2018
comment
Знаете ли вы о каких-либо реальных архитектурах, в которых используется дополненный int? Я не. Концепция какая-то иностранная.   -  person Mark Ransom    schedule 19.01.2018
comment
@EJP Если оператор дополнения ~ и оператор равенства == работают со всеми битами (включая биты заполнения), и если унарный оператор вычитания - работает только с битами значения, то ~(-1) == 0 будет ложным.   -  person RalphS    schedule 19.01.2018
comment
@MarkRansom: я ожидаю, что компилятор для DSP с фиксированной точкой с 16-битной или 32-битной шиной данных и 40-битным аккумулятором, вероятно, будет включать тип данных, который содержит 40 бит данных и либо 8, либо 24 бита заполнения. . Я не уверен, насколько распространены сегодня 40-битные аккумуляторы, но они позволяют вычислять точную целочисленную сумму до 256 32-битных значений без какой-либо возможности переполнения. Если вам нужна 64-битная сумма большего количества значений, вы можете добавлять группы по 256 за раз. 64-битный аккумулятор будет работать так же хорошо, но будет стоить дороже, а дополнительные преимущества будут незначительными.   -  person supercat    schedule 19.01.2018
comment
@RalphS Но они не работают со всеми битами.   -  person user207421    schedule 19.01.2018
comment
@AnT @ EJP Надеюсь на это, и я пытаюсь найти доказательства этого в стандарте C++14.   -  person RalphS    schedule 19.01.2018
comment
@MarkRansom: основная архитектура, о которой я знаю, - это Burroughs B6500, которая прикрепляет 3-битный тег к каждому 48-битному слову данных. Тег обозначает тип данных, и попытка доступа к нему как к другому типу данных приведет к аппаратному исключению.   -  person Jerry Coffin    schedule 19.01.2018
comment
@JerryCoffin соответствующие ссылки в правой части страницы найдены предыдущий вопрос, в котором также упоминается Cray в качестве примера.   -  person Mark Ransom    schedule 19.01.2018
comment
@MarkRansom: Cray действительно имел биты заполнения, но отличается тем, что не имеет ничего похожего на представление ловушки - биты заполнения полностью игнорировались. Если мы включим это, то большинство современных реализаций также включают биты заполнения. bool действительно имеет только два значения, но всегда занимает как минимум один полный байт, а иногда и больше (в некоторых случаях целое 32-битное слово).   -  person Jerry Coffin    schedule 19.01.2018
comment
@JerryCoffin из цитируемых частей стандарта неясно, предназначалось ли заполнение для применения к представлениям захвата или другим. Я предполагаю, что оба.   -  person Mark Ransom    schedule 19.01.2018
comment
@MarkRansom: я верю в оба, но если вы не можете получить представление-ловушку (или подобное), они становятся почти совершенно неинтересными. Почти все в стандарте, в котором говорится о них, связано с возможностью чего-то вроде записи одному члену объединения, чтения другого члена, и ваша программа будет гудеть вместо того, чтобы просто дать вам несколько битов с неизвестными значениями в них.   -  person Jerry Coffin    schedule 19.01.2018


Ответы (2)


Во-первых, в самом стандарте С++ почти ничего не говорится о битах заполнения. По сути, все обсуждение битов заполнения исходит из базового документа (т. е. стандарта C).

Таким образом, реальный вопрос заключается в том, что стандарт C говорит о вещах. Его сноска 54 дает довольно краткий обзор битов заполнения в целом:

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

Операторы могут сильно изменить отступ. Очевидным случаем может быть бит заполнения, представляющий четность. Если вы измените четность значения, бит четности изменится, чтобы соответствовать.

Часть «альтернативные объектные представления значения» в основном означает, что пока вы остаетесь «в границах», биты заполнения не влияют на ваши результаты. Например, если вы сравниваете два значения, для определения результатов используются только биты представления (6.2.6.1/4):

Два значения (кроме NaN) с одним и тем же представлением объекта сравниваются как равные, но значения, которые сравниваются как равные, могут иметь разные представления объекта.

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

Аналогичным образом, если бы вы взяли два значения, memcpy каждое, в буфер беззнакового символа, некоторые биты этих байтов могли бы сравниваться как неравные, даже если значения, которые они представляли, сравнивались бы равными.

Одно место, где это может вас укусить, даже если вы никогда не используете mempy напрямую, — это некоторые операторы сравнения и обмена. Они используют memcpy и memcmp для базовых операций, поэтому они также подлежат сравнению не равными, даже если представляемые значения равны:

[atomics.types.operations]/23:

Семантика memcpy и memcmp операций сравнения и обмена может привести к неудачному сравнению значений, сравниваемых с оператором ==, если базовый тип имеет биты заполнения, биты прерывания или альтернативные представления одного и того же значения. Таким образом, compare_exchange_strong следует использовать с особой осторожностью. С другой стороны, compare_exchange_weak должен быстро сходиться.

Примечание: две большие кавычки являются описательными, а не нормативными — с нормативной точки зрения биты заполнения почти не имеют значения; почти все, что может раскрыть биты заполнения или их значения, связано с поведением, определенным или неопределенным реализацией. Единственная нормативная цитата здесь та, которая в основном говорит: «Биты заполнения не действуют».

person Jerry Coffin    schedule 18.01.2018
comment
В вашей первой цитате есть: никакая арифметическая операция над допустимыми значениями не может создать представление-ловушку. Интересно, считается ли побитовая операция арифметической операцией или операцией над необработанными байтами. - person RalphS; 19.01.2018
comment
@RalphS: я не думаю, что в стандарте есть такое определение, но они определены с точки зрения арифметики (например, значение результата равно E1 × 2 ^ E2). - person Jerry Coffin; 19.01.2018
comment
К сожалению, это верно только для смен. Теперь, когда я лучше разобрался в проблеме, я хотел бы изменить вопрос на: Гарантирует ли C++14, что побитовые операции над действительным целым числом без знака не могут генерировать представления ловушек?. Ответ в основном Нет. Но можно так предположить, потому что только дурак может написать компилятор, который не соблюдает этот принцип. Можно ли изменить вопрос? (Я здесь новичок.) Вопрос в его текущей формулировке немного бесполезен для тех, кто хочет выполнять математические операции с значениями без знака int. - person RalphS; 20.01.2018
comment
С другой стороны, вопрос в его текущей формулировке может быть полезен для тех, кто хочет выполнить побитовое XOR на огромных участках необработанной памяти (шифр XOR). Использование типа unsigned char в этом случае обязательно (в представлении unsigned char не может быть битов заполнения). - person RalphS; 20.01.2018

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

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

  2. Если все биты объекта целочисленного типа равны нулю, объект следует считать допустимым и должен считывать ноль.

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

person supercat    schedule 18.01.2018
comment
Это что-то из стандарта или где-то еще? Укажите свой источник. - person 1201ProgramAlarm; 19.01.2018
comment
Думаю, я понял. Поправьте меня, если я ошибаюсь, но я думаю, что стандарт С++ не является исчерпывающим. Компилятор может быть на 100% совместимым со стандартом и все равно не иметь никакого смысла. И нет смысла пытаться писать код, который будет корректно работать с таким компилятором. Таким образом, для правильной реализации C++ необходимы две вещи: соблюдение требований, описанных в стандарте и соблюдение правил здравого смысла. Одно из этих правил состоит в том, чтобы убедиться, что побитовые операторы работают так, как все ожидают, без вмешательства битов заполнения. - person RalphS; 19.01.2018
comment
@RalphS: К сожалению, хотя стандарты C и C++ изначально были написаны исходя из предположения, что на авторов компиляторов можно положиться, они проявляют здравый смысл, и не было необходимости предписывать поведение, которое, очевидно, имело бы смысл на определенных платформах, составители компиляторов все чаще рассматривают отсутствие мандатов как указание на то, что программисты вправе ожидать такого поведения, и авторы компиляторов не должны чувствовать себя обязанными их поддерживать. К сожалению, семантика C и C++ размывается во имя оптимизации. - person supercat; 19.01.2018
comment
@ 1201ProgramAlarm: для примитивных объектов и простых старых структур данных (PODS) стандарт требует, чтобы копирование всех байтов значения приводило к копированию значения. Таким образом, если при сохранении значения 42 в unsigned short в его хранилище записывается некоторая последовательность битов (возможно, включая биты заполнения), копирование этой последовательности битов в другой unsigned short должно содержать значение 42. Типы, включая целые типы, могут иметь прерывание представления, которые представляют собой последовательности битов, не представляющие действительные значения. - person supercat; 19.01.2018