Что означает (x ^ 0x1)! = 0?

183

Я наткнулся на следующий фрагмент кода

if( 0 != ( x ^ 0x1 ) )
     encode( x, m );

Что x ^ 0x1значит? Это какая-то стандартная техника?

KodeWarrior
источник
97
Запутывание кода вполне стандартно.
raina77ow
47
Это можно решить с помощью того же подхода, что и «нормальное» уравнение: 0 != (x ^ 1)→ xor с обеих сторон на 1 → (0 ^ 1) != (x ^ 1 ^ 1)→ упрощение →1 != x
Score_Under
4
Я не понимаю, как if (1 != x)сложно писать.
Адриано Вароли Пьяцца
12
Различные комментарии, в первую очередь от @Spook, ясно указывают на то, что typeof xне задан - следовательно, мы не знаем, что это целое число в этой теговой проблеме C ++. Конечно, если это C или xцелое число, ответ прост, но это не дано, и operator ^существует вероятность перегрузки .
chux - Восстановить Монику
11
Я не вижу, как это получает так много голосов ...
laike9m

Ответы:

277

Операция XOR ( x ^ 0x1) инвертирует бит 0. Таким образом, выражение фактически означает: если бит 0 в x равен 0 или любой другой бит в x равен 1, то выражение является истинным.

И наоборот, выражение ложно, если x == 1.

Итак, тест такой же как:

if (x != 1)

и поэтому (возможно) излишне запутывается.

Пол Р
источник
39
Эй, ты не знаешь контекст. Если x - это какой-то битовый флаг, IMO на самом деле более понятно записать его, как сейчас, чем использовать оператор! =.
Spook
40
@Spook: это не просто проверка однобитового флага - это проверка всей ширины x. Если вы просто хотите проверить один бит, то есть более понятные идиомы, такие как использование побитового AND.
Пол Р
115
Излишне запутанный? Разве вы не знаете, что наша работа - запутывать код. Если бы мы написали простой код, который мог бы понять каждый, почему, куда пошла бы религиозная святость наших позиций в мире? Мы вдруг станем простыми рабочими, как и все остальные. Запутывание по своей сути необходимо.
Том
31
На самом деле Спок прав. Тест (x! = 1) не эквивалентен. Код может быть C ++ (а в C ++ ^ может быть оператором, который делает все что угодно). Таким образом, вы не знаете контекст, @Spook прав.
xryl669
82
@TheThom youєt yσυ ωяιтє ιi ¢ ℓєαя tєχt
бобобобо
78
  • ^это побитовая операция XOR
  • 0x1находится 1в шестнадцатеричном виде
  • x ^ 0x1будет инвертировать последний бит x(обратитесь к таблице истинности XOR по ссылке выше, если это вам не понятно).

Таким образом, условие (0 != ( x ^ 0x1 ))будет истинным, если xоно больше 1 или последний бит xравен 0. Это оставляет только x == 1 в качестве значения, при котором условие будет ложным. Так что это эквивалентно

if (x != 1)

PS Адский способ реализации такого простого условия, я мог бы добавить. Не делай этого. И если вам нужно написать сложный код, оставьте комментарий . Я прошу тебя.

Фиолетовый Жираф
источник
7
Это не так x==0; 4 ^ 0x1верно, но 4==0очевидно ложно.
Фред Фу
4
«условие кажется равным if (x == 0)», не так ли x != 1?
Эндрю-Дюфрен
6
Эквивалентность предполагает, что xэто целочисленный тип. Если это floatили double, то я считаю, что выражение даст истинное значение для 1.0 <= x < 2.0. И если xэто пользовательский тип, выражение может возвращать true, если xэто день рождения юго, кенгуру, знаменитого композитора или любое число, которое разделяет не менее трех цифр с текущей долларовой ценой чая в Китае.
Суперкат
4
@supercat Нет operator^для float/ double.
пушистый
1
@supercat: Когда требуется преобразование с плавающей запятой в целое, преобразование неявное (не требует синтаксиса приведения). Но никакие преобразования не запускаются побитовыми операторами, они просто терпят неудачу для типов с плавающей точкой.
Бен Фойгт
49

Это может показаться упрощенным объяснением, но если кто-то хотел бы пройти через это медленно, оно ниже:

^является побитовым оператором XOR в c, c ++ и c #.

Побитовое XOR принимает две битовые комбинации одинаковой длины и выполняет операцию логического исключающего ИЛИ для каждой пары соответствующих битов.

Исключающее ИЛИ - это логическая операция, которая выводит истину всякий раз, когда оба входа различаются (один истинный, другой ложный).

Таблица истинности в виде исключающего Ь :

a           b        a xor b
----------------------------
1           1           0
1           0           1
0           1           1
0           0           0

Итак, давайте проиллюстрируем 0 == ( x ^ 0x1 )выражение на двоичном уровне:

             what? xxxxxxxx (8 bits)
               xor 00000001 (hex 0x1 or 0x01, decimal 1)    
             gives 00000000
---------------------------
the only answer is 00000001

так:

   0 == ( x ^ 0x1 )    =>    x == 1
   0 != ( x ^ 0x1 )    =>    x != 1
jwaliszko
источник
34

Это эксклюзивный оператор OR (XOR). Чтобы понять, как это работает, вы можете запустить этот простой код

    std::cout << "0x0 ^ 0x0 = " << ( 0x0 ^ 0x0 ) << std::endl;
    std::cout << "0x0 ^ 0x1 = " << ( 0x0 ^ 0x1 ) << std::endl;
    std::cout << "0x1 ^ 0x0 = " << ( 0x1 ^ 0x0 ) << std::endl;
    std::cout << "0x1 ^ 0x1 = " << ( 0x1 ^ 0x1 ) << std::endl;

Выход будет

0x0 ^ 0x0 = 0
0x0 ^ 0x1 = 1
0x1 ^ 0x0 = 1
0x1 ^ 0x1 = 0

Так что это выражение

0 != ( x ^ 0x1 )

будет равен true только тогда, когда x! = 0x1.

Это не меняет сам х. Он только проверяет, равен ли х 0 или 1. это выражение может быть изменено на

if ( x != 0x1 )
Влад из Москвы
источник
19

Он проверяет , что xна самом деле не 0x1... xorИНГ xс 0x1приведет к 0 , только если xэто 0x1... это старый трюк в основном используется на языке ассемблера

Ференц Дик
источник
Это быстрее чем != 1?
Непослушные биты
2
в древние времена при выполнении ручной сборки оптимизаций (x86) , если я правильно помню тот xorподход содержал меньше машинный код и выполняется быстрее , чем соответствующие присвоения 0... Однако этот вопрос содержит xorи сравнение, так что я мог думать , что !=может быть Быстрее. Однако я не уверен, что нужно будет увидеть сборку, сгенерированную компилятором.
Ференц Дик
10
@BitFiddlingCodeMonkey: Нет. Если бы XOR был быстрее, чем какой-либо собственный тест на равенство на уровне, ваш компилятор выпустил бы XOR для проверки на равенство. Следовательно, XOR никогда не быстрее, чем тесты на равенство при оптимизации компиляторов. Правило 101 написания быстрого кода гласит: «Не пытайтесь помогать компилятору. В итоге вы получите только нечитаемый код, который медленнее на практике».
Мэтт
@fritzone IIRC, уловки XOR были больше связаны с сохранением регистров, сохранением загрузки регистров b / c, в некоторых случаях литерал мог быть закодирован непосредственно в OP, и некоторые незначительные различия с флагами состояния (но большая часть моей сборки была включена 68к и DSP).
mpdonadio
1
@MPD: Обычно это связано с очисткой регистра (по крайней мере, на 386+). xor eax, eax устанавливает значение eax равным нулю в двух байтах, но mov eax, 0 занимает три или шесть байтов (в зависимости от кодировки) и занимает немного больше времени для декодирования, чем форма xor .
Мэтт
18

^Оператор поразрядного исключающего. И 0x1это число 1, записанное в виде шестнадцатеричной константы.

Таким образом, x ^ 0x1оценивается новое значение, которое совпадает x, но с переброшенным младшим значащим битом.

Код не более чем сравнивает x с 1 очень запутанным и неясным способом.

Дэвид Хеффернан
источник
11

Оператор xor (исключающий или) чаще всего используется для инвертирования одного или нескольких битов. Операция заключается в том, чтобы спросить, является ли один из битов одним, это приводит к следующей таблице истинности (A и B - входные данные, Y - выходные данные):

A    B    Y
0    0    0
0    1    1
1    0    1
1    1    0

Теперь цель этого кода, кажется, состоит в том, чтобы проверить, является ли последний бит равным 1, а остальные равны 0, это равно if ( x != 1 ). Причина этого неясного метода может заключаться в том, что ранее использовались методы манипулирования битами и, возможно, они используются в других местах программы.

Павел
источник
8

^поразрядно xor operatorв c. В вашем случае x имеет xor'ed с 1. Например, xимеет значение 10, тогда 10d ^ 1d ===> 1010b ^ 0001b = 1011b, 1011b == 11dусловие становится истинным.

Чинно
источник
Опечатка в вашем ответе. 10 != 1010
Битва за скрип
@BitFiddlingCodeMonkey: опечатка в вашем комментарии:10 (decimal) == 1010 (binary)
Пол Р
6
@PaulR Как кто-нибудь должен знать, что является десятичным и что является двоичным, если вы не помещаете туда bили что-то еще?
Fitdling Bits
не 0b1010 будет Pythonic способ делать вещи?
TankorSmash
8

Побитовый тест кажется преднамеренным запутыванием, но если лежащие в основе данные являются корпоративными данными из системы мэйнфреймов IBM, то может просто оказаться, что код написан для отражения оригинальной документации. Форматы данных IBM восходят к 1960-м годам и часто кодируют флаги как отдельные биты в слове, чтобы сэкономить память. Поскольку форматы были изменены, байты флага были добавлены в конце существующих записей для обеспечения обратной совместимости. Например, документация для записи SMF может содержать код на языке ассемблера для проверки трех отдельных битов в трех разных словах в одной записи, чтобы определить, что данные являются входным файлом. Я знаю гораздо меньше о внутренностях TCP / IP, но вы также можете найти там битовые флаги.

не определено
источник
7

Оператор ^ является побитовым xor (см. &, |). Результат для битовой пары:

0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0

Итак, выражение,

( x ^ 0x1 )

инвертирует / переворачивает 0-й бит x (оставляя остальные биты без изменений).

Подумайте, может ли x иметь значения помимо 0x0 и 0x1? Когда x является однобитовым полем, оно может иметь только значения 0x0 и 0x1, но когда x является целым (char / short / long / etc), биты, кроме bit0, могут влиять на результат выражения.

Выражение в том виде, в котором оно дано, позволяет битам около бит 0 влиять на результат,

if ( 0 != ( x ^ 0x1 ) )

Который имеет эквивалентную правдивость как это (более простое) выражение,

if ( x ^ 0x1 )

Обратите внимание, что это выражение будет проверять только бит 0,

if( 0x1 & ( x ^ 0x1 ) )

Таким образом, выражение в том виде, в котором оно представлено, действительно объединяет две проверки выражения,

if( ( x & ~0x1 )  //look at all bits besides bit0
||  ( x ^ 0x1 ) ) //combine with the xor expression for bit0

Намерен ли автор проверять только бит 0 и намеревался использовать это выражение,

if( 0x1 & ( x ^ 0x1 ) )

Или автор намеревался объединить значения для bit1-bitN и xor для bit0?

ChuckCottrill
источник
7

Я добавляю новый ответ, потому что никто не объяснил, как получить ответ интуитивно.

Инверсия +есть -.
Инверсия ^есть ^.

Как вы решаете 0 != x - 1для x? Вы + 1в обе стороны: 0 + 1 != x - 1 + 11 != x.
Как вы решаете 0 != x ^ 1для x? Вы ^ 1в обе стороны: 0 ^ 1 != x ^ 1 ^ 11 != x.

user541686
источник
6

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

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

Эд Стауб
источник
4

XOR полезен в перечислении флага C #. Для удаления одиночного флага из значения перечисления необходимо использовать оператор xor (ссылка здесь )

Пример:

[Flags]
enum FlagTest { None 0x0, Test1 0x1, Test2 0x2, Test3 0x4}

FlagTest test = FlagTest.Test2 | FlagTest.Test3;
Console.WriteLine(test); //Out: FlagTest.Test2 | FlagTest.Test3
test = test ^ FlagTest.Test2;
Console.WriteLine(test); //Out: FlagTest.Test3
Igrek.
источник
НО вопрос касается C ++, а не C #
theDmi
@theDmi: НО ТАКЖЕ о битовых манипуляциях и битовой маске. Флаг enum в C # определенно относится к битовой маске.
Игрек.
Кроме того, это может быть полезно и в C ++. См .: Стек 1 и Стек 2
Игрек.
Этот ответ, похоже, не имеет ничего общего с исходным вопросом, за исключением некоторого обсуждения побитового оператора XOR?
Пол Р
4

Есть много хороших ответов, но мне нравится думать об этом проще.

if ( 0 != ( x ^ 0x1 ) );

Прежде всего. Оператор if является ложным, только если аргумент равен нулю. Это означает, что сравнивать не равное нулю бессмысленно.

if ( a != 0 );
// Same as
if ( a );

Так что это оставляет нас с:

if ( x ^ 0x1 );

XOR с одним. То, что делает XOR, по сути, определяет биты, которые отличаются. Таким образом, если все биты одинаковы, он вернет 0. Поскольку 0 - ложь, единственный раз, когда он вернет ложь, - это если все биты одинаковы. Так что будет ложно, если аргументы одинаковы, истина, если они разные ... точно так же, как оператор not равно .

if ( x != 0x1 );

Если факт, единственное различие между ними состоит в том, !=что вернет 0 или 1, в то время как ^вернет любое число, но достоверность результата всегда будет одинаковой. Простой способ думать об этом

(b != c) === !!(b ^ c) // for all b and c

Последнее «упрощение» - это преобразование 0x1в десятичное число, равное 1. Поэтому ваше утверждение эквивалентно:

if ( x != 1 )
Кевин Кокс
источник
1

^ - побитовый оператор XOR

Если х = 1

          00000001   (x)       (decimal 1)
          00000001   (0x1)     (decimal 1)
XOR       00000000   (0x0)     (decimal 0)

здесь 0 == (x ^ 0x1)

Если х = 0

          00000000   (x)       (decimal 0)
          00000001   (0x1)     (decimal 1)
XOR       00000001   (0x1)     (decimal 0)

здесь 0! = (x ^ 0x1)

Таблица истинности xor b:

a           b        a xor b
----------------------------
1           1           0
1           0           1
0           1           1
0           0           0

Код просто означает

Акбар Али
источник
6
Действительно ли необходимо опубликовать еще один дубликат ответа?
Мистик
2
Вы можете сказать другой ответ, но не дублировать. извините, если вы не возражаете
Акбар Али
1

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

Окружающий код может часто ссылаться (x ^ 1)или тест может спрашивать: «Если бы бит 0 был наоборот, эта битовая маска была бы пустой?».

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

Если вы берете выражение из контекста и спрашиваете, что оно делает, вы упускаете из виду основное намерение. С таким же успехом вы можете посмотреть на вывод сборки из компилятора и увидеть, что он просто выполняет прямое сравнение на равенство с 1.

sh1
источник
0

Как я вижу ответы до сих пор пропустить простое правило для обработки XORs. Не вдаваясь в подробности, что ^и что 0xозначает (и if, и !=т. Д.), Выражение 0 != (x^1)можно переработать следующим образом, используя тот факт, что (a^a)==0:

0 != (x^1) <=> [xor left and right side by 1]
(0^1) != (x^1^1) <=>
1 != x
Серж Рогач
источник