Co oznacza (x ^ 0x1)! = 0?


183

Natrafiłem na następujący fragment kodu

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

Co x ^ 0x1znaczy Czy to jakaś standardowa technika?


97
Zaciemnianie kodu jest dość standardowe.
raina77ow

47
Można to rozwiązać za pomocą tego samego podejścia, co w przypadku „normalnego” równania: 0 != (x ^ 1)→ x lub obie strony o 1 → (0 ^ 1) != (x ^ 1 ^ 1)→ uprość →1 != x
Score_Under

4
Nie rozumiem, jak if (1 != x)trudno jest pisać.
Adriano Varoli Piazza

12
Różne komentarze, najpierw @Spook wyraźnie podkreślić, że typez xnie podano - stąd nie wiemy, to jest liczbą całkowitą w tym C ++ oznaczone problem. Jasne, jeśli jest to C lub xliczba całkowita, odpowiedź jest łatwa, ale to nie jest dane i operator ^istnieje możliwość przeładowania .
chux - Przywróć Monikę

11
Nie rozumiem, skąd bierze się tyle głosów poparcia ...
laike9m,

Odpowiedzi:


277

Operacja XOR ( x ^ 0x1) odwraca bit 0. Wyrażenie faktycznie oznacza: jeśli bit 0 x wynosi 0 lub dowolny inny bit x wynosi 1, to wyrażenie jest prawdziwe.

I odwrotnie, wyrażenie jest fałszywe, jeśli x == 1.

Test jest taki sam jak:

if (x != 1)

i dlatego jest (prawdopodobnie) niepotrzebnie zaciemniony.


39
Hej, nie znasz kontekstu. Jeśli x jest jakąś flagą bitową, IMO jest w rzeczywistości bardziej przejrzyste, aby ją zapisać tak, jak jest teraz, niż używając operatora! =.
Spook

40
@Spook: nie chodzi tylko o testowanie flagi jednobitowej - testuje całą szerokość x. Jeśli chcesz po prostu przetestować pojedynczy bit, istnieją wyraźniejsze idiomy, takie jak użycie bitowego AND.
Paul R

115
Niepotrzebnie zaciemniony? Czy nie wiesz, że naszym zadaniem jest zaciemnianie kodu. Gdybyśmy napisali prosty kod, który każdy mógłby zrozumieć, dlaczego, dokąd poszedłaby świętość religijna naszych pozycji na świecie? Nagle zostalibyśmy zwykłymi pracownikami, jak wszyscy inni. Zaciemnianie jest z natury konieczne.
Thom,

31
W rzeczywistości Spook ma rację. Test (x! = 1) nie jest równoważny. Kodem może być C ++ (w C ++ ^ może być operatorem, który robi wszystko). Więc nie znasz kontekstu, @Spook ma rację.
xryl669,

82
@TheThom уєт уσυ ωяιтє ιи ¢ ℓєαя тєχт
bobobobo

78
  • ^to bitowa operacja XOR
  • 0x1jest 1w zapisie szesnastkowym
  • x ^ 0x1odwróci ostatni bit x(jeśli nie jest to dla ciebie jasne, zapoznaj się z tabelą prawdy XOR w powyższym linku).

Zatem warunek (0 != ( x ^ 0x1 ))będzie spełniony, jeśli xjest większy niż 1 lub jeśli ostatni bit xwynosi 0. Co pozostawia tylko x == 1 jako wartość, przy której warunek będzie fałszywy. Jest to więc równoważne z

if (x != 1)

PS Do cholery sposób na wdrożenie tak prostego warunku, mógłbym dodać. Nie rób tego A jeśli musisz napisać skomplikowany kod, zostaw komentarz . Błagam Cię.


7
To nie jest x==0; 4 ^ 0x1jest prawdą, ale 4==0oczywiście jest fałszywe.
Fred Foo,

4
„warunek wydaje się być równy if (x == 0)”, prawda x != 1?
Andrew-Dufresne,

6
Równoważność zakłada, że xjest to typ integralny. Jeśli jest to floatlub double, to uważam, że wyrażenie dałoby prawdę 1.0 <= x < 2.0. A jeśli xjest typem zdefiniowanym przez użytkownika, wyrażenie może zwrócić wartość true, jeśli xjest to Yugo, kangur, urodziny słynnego kompozytora lub dowolna liczba, która dzieli co najmniej trzy cyfry z bieżącą ceną herbaty w Chinach wyrażoną w dolarach.
supercat,

4
@ superuper Nie ma operator^dla float/ double.
puszysty

1
@ superupat: Gdy wymagana jest konwersja z liczb zmiennoprzecinkowych na liczbę całkowitą, konwersja jest niejawna (nie wymaga składni rzutowania). Ale operatory bitowe nie wywołują żadnej konwersji, po prostu zawodzą w przypadku typów zmiennoprzecinkowych.
Ben Voigt,

49

Może to wydawać się uproszczonym wyjaśnieniem, ale jeśli ktoś chciałby przejść przez to powoli, jest poniżej:

^jest bitowym operatorem XOR w c, c ++ i c #.

Bitowy XOR przyjmuje dwa wzorce bitów o równej długości i wykonuje logiczną operację wyłączności LUB na każdej parze odpowiednich bitów.

Wyłączne OR jest logiczną operacją, która generuje wartość true, gdy oba dane wejściowe się różnią (jedno jest prawdziwe, drugie fałszywe).

Tabela prawdy z pomocą xor B :

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

Zilustrujmy więc 0 == ( x ^ 0x1 )wyrażenie na poziomie binarnym:

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

więc:

   0 == ( x ^ 0x1 )    =>    x == 1
   0 != ( x ^ 0x1 )    =>    x != 1

34

Jest to wyłączny operator OR (XOR). Aby zrozumieć, jak to działa, możesz uruchomić ten prosty kod

    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;

Wyjście będzie

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

Więc to wyrażenie

0 != ( x ^ 0x1 )

będzie równa prawda tylko wtedy, gdy x! = 0x1.

Nie zmienia samego x. Sprawdza tylko, czy x jest równe 0 lub 1. to wyrażenie można zmienić na

if ( x != 0x1 )

19

Sprawdza on, że xw rzeczywistości nie 0x1... xoring xz 0x1spowodują 0 tylko wtedy, gdy xjest 0x1... jest to stary trik stosowany głównie w asemblerze


Czy to jest szybsze niż != 1?
Fiddling Bits,

2
w czasach starożytnych podczas ręcznej optymalizacji montażu (x86), jeśli dobrze pamiętam, xorpodejście zawierało mniej kodu maszynowego i było wykonywane szybciej niż odpowiednie przypisanie do 0... jednak to pytanie zawiera xorORAZ porównanie, więc mogę pomyśleć, że !=może to być szybciej. Nie jestem jednak pewien, czy powinienem zobaczyć zestaw generowany przez kompilator.
Ferenc Deak,

10
@BitFiddlingCodeMonkey: Nie. Jeśli XOR byłby szybszy niż jakiś test równości na poziomie natywnym, twój kompilator wyemitowałby XOR w celu sprawdzenia równości. Dlatego XOR nigdy nie są szybsze niż testy równości w optymalizacji kompilatorów. Reguła 101 pisania szybkiego kodu brzmi: „nie próbuj pomagać kompilatorowi. Skończysz tylko na tworzeniu nieczytelnego kodu, który w praktyce jest wolniejszy”.
Matt

@fritzone IIRC, sztuczki XOR miały więcej wspólnego z zapisywaniem rejestrów, zapisywaniem obciążenia rejestru b / c dosłowność w niektórych przypadkach można było zakodować bezpośrednio w OP, a także pewne subtelne różnice z flagami statusu (ale większość mojego zestawu była włączona 68k i DSP).
mpdonadio,

1
@MPD: Zwykle ma to związek z czyszczeniem rejestru (przynajmniej na 386+). xor eax, eax ustawia eax na zero w dwóch bajtach, ale mov eax, 0 zajmuje trzy lub sześć bajtów (w zależności od kodowania) i trwa nieco dłużej dekodowanie niż forma xor .
Matt

18

^Operator bitowy XOR. I 0x1jest liczbą 1zapisaną jako stała szesnastkowa.

Tak więc x ^ 0x1ocenia na nową wartość, która jest taka sama jak x, ale z najmniej znaczącym odwróconym bitem.

Kod robi tylko porównanie x z 1, w bardzo zawiły i niejasny sposób.


11

Operator xor (wyłączny lub) jest najczęściej używany do odwracania jednego lub więcej bitów. Operacja polega na zapytaniu, czy dokładnie jeden z bitów to jeden, prowadzi to do następującej tabeli prawdy (A i B są wejściami, Y jest wyjściowy):

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

Teraz celem tego kodu wydaje się sprawdzenie, czy dokładnie ostatni bit ma wartość 1, a pozostałe mają wartość 0, co jest równe if ( x != 1 ). Przyczyną tej niejasnej metody może być użycie wcześniejszych technik manipulacji bitami i być może wykorzystywanie innych miejsc w programie.


8

^jest bitowe xor operatorw c. W twoim przypadku x jest xor'ed z 1. na przykład xma wartość 10, 10d ^ 1d ===> 1010b ^ 0001b = 1011b, 1011b == 11dwięc warunek staje się prawdziwy.


Literówka w odpowiedzi. 10 != 1010
Fiddling Bits,

@BitFiddlingCodeMonkey: literówka w twoim komentarzu:10 (decimal) == 1010 (binary)
Paul R

6
@PaulR Jak ktokolwiek powinien wiedzieć, co jest dziesiętne, a co dwójkowe, jeśli nie umieścisz tam bani czegoś takiego?
Fiddling Bits,

czy 0b1010 nie byłby Pythońskim sposobem robienia rzeczy?
TankorSmash,

8

Test bitowy wydaje się celowym zaciemnianiem, ale jeśli dane leżące u podstaw są danymi korporacyjnymi z systemu mainframe IBM, może to być po prostu napisanie kodu odzwierciedlającego oryginalną dokumentację. Formaty danych IBM sięgają lat 60. XX wieku i często kodują flagi jako pojedyncze bity w jednym słowie, aby zaoszczędzić miejsce. Gdy formaty zostały zmodyfikowane, bajty flag zostały dodane na końcu istniejących rekordów, aby zachować kompatybilność wsteczną. Na przykład dokumentacja rekordu SMF może pokazywać kod języka asemblera do testowania trzech pojedynczych bitów w ramach trzech różnych słów w jednym rekordzie w celu stwierdzenia, że ​​dane są plikiem wejściowym. Wiem znacznie mniej o wewnętrznych elementach TCP / IP, ale możesz tam również znaleźć flagi bitowe.


7

Operator ^ jest bitowym xorem (patrz &, |). Wynik dla pary bitów to:

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

Więc wyrażenie

( x ^ 0x1 )

odwraca / odwraca 0-ty bit x (pozostawiając inne bity bez zmian).

Zastanów się, czy x może mieć wartości oprócz 0x0 i 0x1? Gdy x jest polem pojedynczego bitu, może mieć tylko wartości 0x0 i 0x1, ale gdy x jest liczbą całkowitą (char / short / long / etc), bity oprócz bitu 0 mogą wpływać na wynik wyrażenia.

Podane wyrażenie pozwala bitom obok bitu 0 wpływać na wynik,

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

Która ma równoważną prawdomówność jak to (prostsze) wyrażenie,

if ( x ^ 0x1 )

Zauważ, że to wyrażenie bada tylko bit0,

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

Zatem przedstawione wyrażenie naprawdę łączy dwie kontrole wyrażenia,

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

Czy autor zamierzał sprawdzić tylko bit0 i zamierzał użyć tego wyrażenia,

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

A może autor zamierzał połączyć wartości dla bit1-bitN i xor z bit0?


7

Dodam nową odpowiedź, ponieważ nikt tak naprawdę nie wyjaśnił, jak uzyskać odpowiedź intuicyjnie.

Odwrotnością +jest -.
Odwrotnością ^jest ^.

W jaki sposób można rozwiązać 0 != x - 1za x? Po + 1obu stronach: 0 + 1 != x - 1 + 11 != x.
W jaki sposób można rozwiązać 0 != x ^ 1za x? Po ^ 1obu stronach: 0 ^ 1 != x ^ 1 ^ 11 != x.


6

Sądzę, że są w nim inne bity lub wartości pól bitowych x, a to ma na celu sprawdzenie, czy ustawiony jest tylko bit niskiego rzędu. W tym kontekście domyślam się, że jest to ustawienie domyślne, a zatem kodowanie tego i niektórych powiązanych m(prawdopodobnie droższych do kodowania) można pominąć, ponieważ oba muszą być wartością domyślną, zainicjowaną w konstruktorze lub podobnym.

W jakiś sposób dekoder musi być w stanie wywnioskować, że brakuje tych wartości. Jeśli znajdują się na końcu jakiejś struktury, można to przekazać za pomocą lengthwartości, która jest zawsze obecna.


4

XOR jest użyteczny w enum flagi C #. Aby usunąć pojedynczą flagę z wartości wyliczeniowej, konieczne jest użycie operatora xor ( tutaj )

Przykład:

[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

ALE pytanie dotyczy C ++, a nie C #
theDmi

@ theDmi: ALE także o manipulacji bitami i masce bitowej. Wyliczanie flagi w C # zdecydowanie dotyczy maski bitowej.
Igrek.

Dodatkowo może być przydatny również w C ++. Patrz: Stos 1 i Stos 2
Igrek.

Wydaje się, że ta odpowiedź nie ma nic wspólnego z pierwotnym pytaniem poza dyskusją na temat bitowego operatora XOR?
Paul R

4

Istnieje wiele dobrych odpowiedzi, ale lubię o tym myśleć w prostszy sposób.

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

Po pierwsze. Instrukcja if jest fałszywa tylko wtedy, gdy argument wynosi zero. Oznacza to, że porównywanie nie równe zeru jest bezcelowe.

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

Pozostaje nam więc:

if ( x ^ 0x1 );

XOR z jednym. XOR w zasadzie wykrywa różne bity. Tak więc, jeśli wszystkie bity są takie same, zwróci 0. Ponieważ 0 jest fałszem, jedyny raz zwróci fałsz, jeśli wszystkie bity są takie same. Więc to będzie fałsz, jeśli argumenty będą takie same, prawda, jeśli będą różne ... tak jak operator nie równy .

if ( x != 0x1 );

Jeśli faktem, jedyną różnicą między nimi jest to, że !=zwróci 0 lub 1, podczas gdy ^zwróci dowolną liczbę, ale prawdziwość wyniku zawsze będzie taka sama. Łatwym sposobem na przemyślenie tego jest.

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

Końcowe „uproszczenie” jest konwertowane 0x1na dziesiętne, które wynosi 1. Dlatego twoja instrukcja jest równoważna z:

if ( x != 1 )

1

^ jest bitowym operatorem XOR

Jeśli x = 1

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

tutaj 0 == (x ^ 0x1)

Jeśli x = 0

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

tutaj 0! = (x ^ 0x1)

Tabela prawdy dla xor b:

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

Kod po prostu znaczy


6
Czy naprawdę konieczne jest opublikowanie jeszcze jednej zduplikowanej odpowiedzi?
Mysticial

2
możesz powiedzieć inną odpowiedź, ale nie powielać się. przepraszam, jeśli masz coś przeciwko
akbar ali

1

Standardową techniką, która może być tutaj użyta, jest powtórzenie idiomu, który pojawia się w otaczającym kontekście dla jasności, zamiast zaciemnianie go poprzez zastąpienie go idiomem, który jest arytmetycznie prostszy, ale kontekstowo bez znaczenia.

Otaczający kod może często się odwoływać (x ^ 1)lub test może zapytać „jeśli bit 0 byłby odwrotnie, czy ta maska ​​bitowa byłaby pusta?”.

Biorąc pod uwagę, że warunek powoduje, że coś się encode()edytuje, może być tak, że w kontekście domyślny stan bitu 0 został odwrócony przez inne czynniki i potrzebujemy tylko zakodować dodatkowe informacje, jeśli któryś z bitów odbiega od wartości domyślnej (zwykle zero) ).

Jeśli wyjmiesz wyrażenie z kontekstu i zapytasz, co robi, przeoczysz leżącą u jego podstaw intencję. Równie dobrze możesz spojrzeć na dane wyjściowe zestawu z kompilatora i zobaczyć, że po prostu dokonuje bezpośredniego porównania równości z 1.


0

Jak widzę, odpowiedzi do tej pory brakuje prostej zasady obsługi XORs. Bez wchodzenia w szczegóły co ^i 0xśrednia (i if, i !=etc), wyrażenie 0 != (x^1)może być przerobione w następujący sposób wykorzystując fakt, że (a^a)==0:

0 != (x^1) <=> [xor left and right side by 1]
(0^1) != (x^1^1) <=>
1 != x
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.