Являются ли результаты побитовых операций над целочисленными типами со знаком хорошо определенными?

Рассмотрим этот код:

using integer = int; // or any other fundamental integral type
using unsigned_integer = typename std::make_unsigned<integer>::type;
constexpr integer bits = std::numeric_limits<unsigned_integer>::digits;
integer value = -42; // or any value
integer mask = static_cast<integer>(1)<<static_cast<integer>(bits-1);
bool result_and = value & mask;
bool result_or = value | mask;
bool result_xor = value ^ mask;

Мне интересно, насколько хорошо эти операции определены в соответствии со стандартом. Есть ли у меня гарантия, что я получу одинаковые результаты для всех архитектур? Я уверен, что будет работать со знаковым битом на всех архитектурах, где этот знаковый бит равен 0 для положительных чисел и 1 для отрицательных чисел?

7 голосов | спросил Vincent 21 +03002015-10-21T08:15:05+03:00312015bEurope/MoscowWed, 21 Oct 2015 08:15:05 +0300 2015, 08:15:05

3 ответа


0

Результаты побитового и побитового или побитового xor в настоящее время недостаточно определены в стандарте, в частности, термин побитовый никогда не определяется. У нас есть отчет о дефектах 1857: дополнительные вопросы о битах который освещает эту проблему и говорит:

  

Спецификация побитовых операций в 5.11 [expr.bit.and],   5.12 [expr.xor] и 5.13 [expr.or] использует неопределенный термин «побитовый» в описании операций, не указывая, является ли это   значение или представление объекта, которое находится в поле зрения.

     

Частью разрешения этого может быть определение «бита» (который в настоящее время в C ++ не определен) как значение заданной степени 2.

и разрешение было:

  

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

Результатом которого стал сводный отчет о дефектах 1943 года: не указан значение «бит» .

Результат смещения влево подписанного типа будет зависеть от базового представления. Это можно увидеть из отчета о дефектах 1457: неопределенное поведение в сдвиг влево , который четко определил сдвиг влево в бит знака и говорит:

  

Текущая редакция пункта 5.8 [expr.shift] делает его неопределенным   поведение для создания наиболее отрицательного целого числа данного типа    сдвиг влево (со знаком) 1 в знаковый бит, хотя это не   необычно сделано и работает правильно на большинстве   (двойное дополнение) архитектуры :

     
    

... если E1 имеет тип со знаком и неотрицательное значение, а E1 E 2E2 представимо в типе результата, то это результирующее значение;     в противном случае поведение не определено.

  
     

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

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

ответил Shafik Yaghmour 21 +03002015-10-21T12:36:50+03:00312015bEurope/MoscowWed, 21 Oct 2015 12:36:50 +0300 2015, 12:36:50
0

Относительно левого и правого операторов сдвига, из стандартного раздела C ++ 5.8:

  

Поведение не определено, если правый операнд отрицательный или больше   чем или равно длине в битах повышенного левого операнда.

Затем говорится, что левый оператор сдвига E1 ​​<<E2 приводит к неопределенному поведению, когда выполняются все следующие условия:

  • Левый операнд имеет тип со знаком.
  • Левый операнд имеет отрицательное значение или неотрицательное значение, так что E1 × 2 ^ E2 не представляется в результирующем типе.

Также касается оператора смещения вправо E1>> E2, поведение зависит от реализации, если левый операнд имеет тип со знаком и отрицательное значение.

Побитовые операторы AND, XOR и OR хорошо определены для всех целочисленных типов. Это указано в разделах 5.11, 5.12 и 5.13 соответственно.

Тем не менее, обратите внимание, что представление целых значений со знаком может быть либо дополнением двух, либо дополнением единиц, либо величиной со знаком. Большинство компиляторов используют представление дополнения к двум. К ним относятся gcc, VC ++, icl и Clang.

ответил Hadi Brais 21 +03002015-10-21T08:40:46+03:00312015bEurope/MoscowWed, 21 Oct 2015 08:40:46 +0300 2015, 08:40:46
0

Операторы &, | и ^ являются побитовыми и имеют дело с отдельными битами, поэтому они будут делать именно то, что вы написали: примените mask

Оператор

сдвиг влево << немного сложнее. Это приведет к неопределенному поведению, если вы сдвинете отрицательное значение или сдвинете 1, чтобы обозначить позицию бита или выше.

static_cast<integer>(1)<<static_cast<integer>(bits-1);

Похоже, вы сдвигаете 1, чтобы подписать битовую позицию, и это неопределенное поведение.

ответил Stas 21 +03002015-10-21T08:49:33+03:00312015bEurope/MoscowWed, 21 Oct 2015 08:49:33 +0300 2015, 08:49:33

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132