logical c++ boolean bitwise-operators

c++ - logical xor in c



Usar operadores bit a bit para Booleanos en C++ (8)

Las cejas levantadas deberían decirte lo suficiente como para dejar de hacerlo. No escribe el código para el compilador, primero lo escribe para sus compañeros programadores y luego para el compilador. Incluso si los compiladores funcionan, sorprender a otras personas no es lo que quieres: los operadores bit a bit son para operaciones de bits, no para bools.
Supongo que también comes manzanas con un tenedor? Funciona pero sorprende a la gente, así que es mejor no hacerlo.

¿Hay alguna razón para no usar los operadores bit a bit &, |, y ^ para los valores "bool" en C ++?

A veces me encuentro con situaciones en las que quiero que una de las dos condiciones sea verdadera (XOR), así que simplemente arrojo el operador ^ a una expresión condicional. A veces también quiero que se evalúen todas las partes de una condición, independientemente de si el resultado es verdadero o no (en lugar de cortocircuitar), así que utilizo & y |. También necesito acumular valores booleanos a veces, y & = y | = pueden ser bastante útiles.

Obtuve algunas cejas al hacer esto, pero el código sigue siendo significativo y más limpio de lo que sería de otra manera. ¿Hay alguna razón para NO usar estos para bools? ¿Hay compiladores modernos que dan malos resultados para esto?


Patrick hizo buenos comentarios y no voy a repetirlos. Sin embargo, podría sugerir que se reduzcan las declaraciones ''if'' a un inglés legible siempre que sea posible mediante el uso de variables booleanas bien nombradas. Por ejemplo, y esto está usando operadores booleanos, pero también podría usar bitwise y nombrar los bools de manera apropiada:

bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here bool onlyBIsTrue = (b && !a); // and not need this second line if (onlyAIsTrue || onlyBIsTrue) { .. stuff .. }

Puede pensar que usar un booleano parece innecesario, pero ayuda con dos cosas principales:

  • Su código es más fácil de entender porque el booleano intermedio para la condición ''si'' hace que la intención de la condición sea más explícita.
  • Si está utilizando un código no estándar o inesperado, como operadores bit a bit en valores booleanos, las personas pueden ver fácilmente por qué lo ha hecho.

EDITAR: No dijiste explícitamente que querías los condicionales para las declaraciones ''if'' (aunque esto parece más probable), esa fue mi suposición. Pero mi sugerencia de un valor booleano intermedio sigue en pie.


IIRC, muchos compiladores C ++ advertirán cuando intente lanzar el resultado de una operación bit a bit como bool. Tendría que usar un molde de tipo para hacer feliz al compilador.

Usar una operación bit a bit en una expresión if serviría la misma crítica, aunque quizás no por el compilador. Cualquier valor distinto de cero se considera verdadero, por lo que algo como "if (7 & 3)" será verdadero. Este comportamiento puede ser aceptable en Perl, pero C / C ++ son lenguajes muy explícitos. Creo que la ceja de Spock es la debida diligencia. :) Añadiría "== 0" o "! = 0" para que quede perfectamente claro cuál era tu objetivo.

Pero de todos modos, parece una preferencia personal. Me gustaría ejecutar el código a través de pelusa o herramienta similar y ver si también piensa que es una estrategia imprudente. Personalmente, se lee como un error de codificación.


creo

a != b

es lo que quieres


Contrariamente a la respuesta de Patrick, C ++ no tiene operador ^^ para realizar un cortocircuito exclusivo o. Si lo piensa por un segundo, tener un operador ^^ no tendría sentido de todos modos: con exclusive o, el resultado siempre depende de ambos operandos. Sin embargo, la advertencia de Patrick acerca de los tipos "booleanos" no booleanos tiene igual validez cuando se comparan 1 & 2 con 1 && 2 . Un ejemplo clásico de esto es la función de Windows GetMessage() , que devuelve un BOOL tri-state: distinto de cero, 0 o -1 .

Usando & lugar de && y | en lugar de || no es un error tipográfico poco común, por lo que si lo hace deliberadamente, merece un comentario que diga por qué.


Dos razones principales. En resumen, considere detenidamente; puede haber una buena razón para ello, pero si hay MUY explícito en sus comentarios porque puede ser frágil y, como usted mismo dice, las personas no están acostumbradas a ver códigos como este.

Bitwise xor! = Logical xor (excepto para 0 y 1)

En primer lugar, si opera con valores distintos de false y true (o 0 y 1 , como números enteros), el operador ^ puede introducir un comportamiento no equivalente a un xor lógico. Por ejemplo:

int one = 1; int two = 2; // bitwise xor if (one ^ two) { // executes because expression = 3 and any non-zero integer evaluates to true } // logical xor; more correctly would be coded as // if (bool(one) != bool(two)) // but spelled out to be explicit in the context of the problem if ((one && !two) || (!one && two)) { // does not execute b/c expression = ((true && false) || (false && true)) // which evaluates to false }

Gracias al usuario @Patrick por expresar esto primero.

Orden de operaciones

Segundo, | , & y ^ , como operadores bit a bit, no cortocircuiten. Además, múltiples operadores bit a bit encadenados juntos en una sola instrucción, incluso con paréntesis explícitos, pueden reordenarse optimizando compiladores, ya que las 3 operaciones son normalmente conmutativas. Esto es importante si el orden de las operaciones es importante.

En otras palabras

bool result = true; result = result && a() && b(); // will not call a() if result false, will not call b() if result or a() false

no siempre dará el mismo resultado (o estado final) que

bool result = true; result &= (a() & b()); // a() and b() both will be called, but not necessarily in that order in an // optimizing compiler

Esto es especialmente importante porque es posible que no controle los métodos a() y b() , o que alguien más pueda aparecer y cambiarlos más tarde sin entender la dependencia, y causar un error desagradable (y, a menudo, solo release-build).


Desventajas de los operadores de nivel de bits.

Usted pregunta:

"¿Hay alguna razón para no usar los operadores bit a bit & , | , y ^ para valores "bool" en C ++? "

Sí, los operadores lógicos , ¡que son los operadores booleanos de alto nivel integrados ! , && y || , ofrece las siguientes ventajas:

  • Conversión garantizada de argumentos a bool , es decir, a 0 y 1 valor ordinal.

  • Evaluación garantizada de cortocircuito donde la evaluación de expresión se detiene tan pronto como se conoce el resultado final.
    Esto se puede interpretar como una lógica de valor de árbol, con verdadero , falso e indeterminado .

  • Los equivalentes textuales legibles not , and y or , incluso si yo no los uso yo mismo.
    Como señala el lector Antimony en un comentario, también los operadores de nivel de bits tienen tokens alternativos, a saber, bitand , bitor , xor y compl , pero en mi opinión estos son menos legibles que and , or not .

En pocas palabras, cada ventaja de los operadores de alto nivel es una desventaja de los operadores de nivel de bits.

En particular, dado que los operadores en modo bit carecen de conversión de argumento a 0/1, se obtiene, por ejemplo, 1 & 20 , mientras que 1 && 2true . También ^ , bitwise exclusive o, puede portarse mal de esta manera. Considerados como valores booleanos 1 y 2 son los mismos, es decir, true , pero considerados como patrones de bit, son diferentes.

Cómo expresar lógico, ya sea / o en C ++.

A continuación, proporciona un poco de antecedentes para la pregunta,

"A veces me encuentro con situaciones en las que quiero que una de las dos condiciones sea verdadera (XOR), así que simplemente arrojo el operador ^ a una expresión condicional".

Bueno, los operadores bit a bit tienen mayor prioridad que los operadores lógicos. Esto significa en particular que en una expresión mixta como

a && b ^ c

obtienes el resultado tal vez inesperado a && (b ^ c) .

En cambio escribe solo

(a && b) != c

expresando más concisamente lo que quieres decir.

Para el argumento múltiple, ya sea / o no hay operador de C ++ que haga el trabajo. Por ejemplo, si escribe a ^ b ^ c esa no es una expresión que dice "o a , c es verdadero". En su lugar, dice: "Un número impar de a , c son verdaderos", que podría ser 1 de ellos o los 3 ...

Para expresar el general ya sea / o cuando a , c son de tipo bool , solo escriba

(a + b + c) == 1

o, con argumentos no bool , bool a bool :

(!!a + !!b + !!c) == 1

Usando &= para acumular resultados booleanos.

Usted elabora más,

"También necesito acumular valores booleanos a veces, y &= y |=? puede ser bastante útil ".

Bueno, esto corresponde a verificar si se cumplen todas o todas las condiciones, y la ley de Morgan te dice cómo pasar de una a la otra. Es decir, solo necesitas uno de ellos. En principio, podría usar *= como un &&= (como descubrió el bueno de George Boole, el AND lógico y puede expresarse fácilmente como una multiplicación), pero creo que eso dejaría perplejos y quizás confundiría a los encargados del código.

Considera también:

struct Bool { bool value; void operator&=( bool const v ) { value = value && v; } operator bool() const { return value; } }; #include <iostream> int main() { using namespace std; Bool a = {true}; a &= true || false; a &= 1234; cout << boolalpha << a << endl; bool b = {true}; b &= true || false; b &= 1234; cout << boolalpha << b << endl; }

Salida con Visual C ++ 11.0 y g ++ 4.7.1:

true false

La razón de la diferencia en los resultados es que el nivel de bits &= no proporciona una conversión a bool de su argumento del lado derecho.

Entonces, ¿cuál de estos resultados desea para su uso de &= ?

Si el primero es true , entonces mejor defina un operador (por ejemplo, como se indica arriba) o una función nombrada, o use una conversión explícita de la expresión del lado derecho, o escriba la actualización en su totalidad.


|| y && son operadores booleanos y los integrados tienen la garantía de devolver true o false . Nada más.

| , yy ^ son operadores bit a bit. Cuando el dominio de los números con los que opera es solo 1 y 0, entonces son exactamente iguales, pero en los casos en que sus booleanos no son estrictamente 1 y 0, como en el caso del lenguaje C, puede terminar con algún comportamiento no quisiste Por ejemplo:

BOOL two = 2; BOOL one = 1; BOOL and = two & one; //and = 0 BOOL cand = two && one; //cand = 1

En C ++, sin embargo, se garantiza que el tipo de bool sea ​​solo true o false (que se convierte implícitamente en respectivamente 1 y 0 ), por lo que es menos preocupante desde esta postura, pero el hecho de que las personas no están acostumbradas a ver tales cosas en el código es un buen argumento para no hacerlo. Simplemente diga b = b && x y termine con esto.