¿Cuándo es apropiado usar un operador bit a bit en una expresión condicional?

15

Primero, algunos antecedentes: soy un maestro de TI en formación y estoy tratando de presentar a los operadores booleanos de Java a mi clase de décimo grado. Mi maestro-mentor examinó una hoja de trabajo que preparé y comentó que podría dejarles usar solo una & o | para denotar a los operadores, porque "hacen lo mismo".

Soy consciente de la diferencia entre & y &&.
& es un operador bit a bit destinado a ser utilizado entre enteros, para realizar "bit-twiddling".
&& es un operador condicional destinado a usarse entre valores booleanos.

Para probar el punto de que estos operadores no siempre "hacen lo mismo", me propuse encontrar un ejemplo en el que el uso de los valores booleanos entre bits produjera un error. Encontré este ejemplo

boolean bitwise;
boolean conditional;
int i=10, j=12;
bitwise = (i<j) | ((i=3) > 5); // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i<j) || (i=3) > 5 ;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);
i=10; 
bitwise = (i>j) & (i=3) > 5;   // value of i after oper: 3
System.out.println(bitwise+ " "+ i);
i=10; 
conditional = (i>j) && (i=3) > 5;  // value of i after oper: 10
System.out.println(conditional+ " "+ i);

Este ejemplo muestra que si un valor tiene que cambiarse en la segunda mitad de la expresión, esto conduciría a una diferencia entre los resultados, ya que bitwise es un operador ansioso, mientras que el condicional se comporta como un cortocircuito (no evalúa el segundo mitad, si la primera mitad es falsa en el caso de && y verdadera en el caso de ||).

Tengo un problema con este ejemplo. ¿Por qué querrías cambiar un valor al mismo tiempo que lo comparas? No parece una forma robusta de codificar. Siempre he sido reacio a realizar múltiples operaciones en una sola línea en mi código de producción. Parece algo que haría un "vaquero codificador" sin conciencia en cuanto a la facilidad de mantenimiento de su código. Sé que en algunos dominios el código debe ser lo más compacto posible, pero ¿seguramente esta es una mala práctica en general?

Puedo explicar mi elección de fomentar el uso de && y || sobre y y | porque esta es una convención de codificación aceptada en ingeniería de software .

Pero, ¿podría alguien darme un ejemplo mejor, incluso del mundo real, del uso de un operador bit a bit en una expresión condicional?

Deerasha
fuente

Respuestas:

16

es apropiado cuando realiza una operación de enmascaramiento

if ((a & b)> 0) {...}

donde a y b son enteros

|| y | y && y & no son intercambiables

El | y & casi nunca aparecerán en una expresión condicional por sí mismos (el punto del enlace que incluyó es que tales cosas son a menudo errores)

EDITAR: No discutas con tu mentor; incluso si ganas, pierdes. En cambio, explique que no quiere confundir a los estudiantes mezclando operadores lógicos y operadores bit a bit en la misma lección. Podría explicar que si i = 3 y j = 2, entonces i & j = 2, mientras que i && j es un error. Sin embargo, la explicación más simple es que está enseñando operadores booleanos (lógicos), por lo que arrojar equivalentes bit a bit en casos especiales es una distracción del punto principal de la lección. No hay necesidad de hacer que el mentor esté "equivocado", y no es necesario producir contraejemplos. El enfoque de la lección está en los operadores booleanos, no en los operadores bit a bit.

Como corolario, cuando comienzas a enseñar operadores bit a bit, no es necesario mostrar los casos especiales donde + y - producen los mismos resultados que & y |

Steven A. Lowe
fuente
Sin embargo, el código anterior no está mal, puede ser confuso pero, tal como está, ese código no contiene un error, ¿verdad? Estoy de acuerdo en que usar operadores bit a bit de esta manera no es muy legible, no me gustaría si lo viera en una revisión de código, pero es Java legal (y también C #, ignorando System.out.println).
Steve
Gracias por responder a @Steven A. Lowe. Dijiste "|| y | y && y & no son intercambiables" pero bitwise = !(true & true == false);y condition = !(true && true == false);ambos se evaluarán como verdaderos, ¿entonces en este caso son intercambiables? Tal vez sintácticamente, ya que el código aún se compila. Estoy de acuerdo en que se usan semánticamente para diferentes cosas, como mencioné en el párrafo 2. ¡Dices eso! y & "casi nunca aparecen en un condicional por sí mismos". Estoy buscando estos casos de "casi nunca" y me pregunto si existen legítimamente.
Deerasha
@Deerasha: || y && operan solo en booleanos. & y | operar en enteros y booleanos: no tiene mucho sentido usar operadores bit a bit (destinados a operar en varios bits ) para manipular booleanos, y hacerlo con abandono puede generar código confuso y comportamientos inesperados (es decir, si accidentalmente usa un entero en lugar de un booleano, los operadores bit a bit no se quejarán)
Steven A. Lowe
Sí @Steve Haigh, el compilador no lo rechaza, pero no es la forma correcta de usarlos, a juzgar por el estándar de codificación publicado al que me vinculé. ¿Puedo descansar cómodamente descartándolo porque no cumple con un estándar de codificación, o Java debería indicar que este es un uso incorrecto?
Deerasha
2
@ Steven A. Lowe: si i = 3 y j = 2, entonces i & j = 2, mientras que i && j es un error ¡ Esto es brillante! Es simple y hace el punto. A nivel de décimo grado, este también es un error probable, ya que todavía se están acostumbrando al tipo booleano y a lo que se aplicarían los operadores. Muchas gracias! Un gran consejo para no discutir con mi mentor tampoco.
Deerasha
12

Los operadores no bit a bit &&y ||son operadores de cortocircuito. En otras palabras, con &&, si el LHS es falso, el RHS nunca será evaluado; con ||si el LHS es verdadero, entonces el RHS nunca será evaluado. Por otro lado, los operadores bit a bit &y |no son de cortocircuito, y siempre evaluarán tanto el LHS como el RHS. De lo contrario, son equivalentes en una ifdeclaración.

El único momento en que puedo ver el valor al usar los operadores que no son de cortocircuito es si el RHS tiene algún tipo de efecto secundario deseable que desea que ocurra en todos los casos. No puedo pensar en un ejemplo específico en el que quieras esto, y no creo que sea una buena práctica, pero esa es la diferencia.

Jonathan
fuente
1
+1. Exactamente correcto, al comparar los operadores lógicos y lógicos de booleanos siempre devuelven el mismo resultado, pero (en Java y C # al menos) solo los operadores lógicos cortocircuitan.
Steve
Gracias por responder. Su párrafo 1 era a lo que estaba tratando de llegar en mi párrafo 4, al afirmar que bitwise es un operador ansioso mientras que el condicional se comporta como un cortocircuito . Su párrafo 2 es la preocupación que describí en mi párrafo 5. Así que soy consciente de la diferencia, pero estoy buscando ese ejemplo específico que ni usted ni yo podemos pensar.
Deerasha
7

La respuesta filosófica general es que el uso de operadores bit a bit para operadores booleanos es atípico y hace que el código sea más difícil de leer. En la práctica (para el código que está en producción), un código más legible es más fácil de mantener y, por lo tanto, más deseable.

Para un uso real de la necesidad de operadores de cortocircuito, vea casos como:

if (args.length > 0 && args[0] == 'test') ....

if (b != NULL && b.some_function()) ...

if (b == NULL || b.some_function()) ...

Este tipo de operaciones se muestran con frecuencia en el código del mundo real.

Kathy Van Stone
fuente
Tfa ¿Cómo le explico a mis 15 años que 1 &es "más difícil de leer" que 2? No tengo un ejemplo en el que los 2 operadores no funcionen de la misma manera para los operandos booleanos. Estoy de acuerdo con su punto sobre un código más legible y más fácil de mantener. Quiero alentarlos a escribir un código hermoso. Pero tener alguna prueba en mi bolsa de herramientas sería más convincente que "porque lo dije". Lo tengo establecido como estándar en ese enlace, y es posible que tenga que confiar solo en eso si no obtengo el ejemplo que estoy buscando. Como le pregunté a @Steve Haigh: ¿Java debería indicar esto como un uso incorrecto?
Deerasha
@Deerasha Estaba pensando más en una discusión con tu mentor. Para la clase, ni siquiera intentes decirles que puedes usar operadores bit a bit para condiciones lógicas.
Kathy Van Stone
4

Utilizará operadores bit a bit si compara las enumeraciones de Bitmask. Por ejemplo, tiene una enumeración de estados y un objeto que puede estar en más de uno de esos estados. En este caso, hará un bit a bit o para asignar más de un estado a su objeto.

por ejemplo state = CONNECTED | IN_PROGRESSdonde CONNECTED could be 0x00000001yIN_PROGRESS 0x00000010

Para obtener más información, busque la documentación de las enumeraciones de la bandera.

pwny
fuente
¡Gracias a ti he aprendido una aplicación de operadores bit a bit que no conocía antes! Pero en este ejemplo, solo se aplica el operador bit a bit. Estoy buscando un fragmento de código bien escrito en el que usar el bit a bit en lugar del condicional conduzca a la compilación, pero a un resultado incorrecto. Es decir, si existe dicho fragmento de código.
Deerasha
No creo que esto pueda suceder en Java, ya que creo que llamar al | o & operadores en valores que no pueden ser bit a bit y'd o or'd solo realiza una lógica | o &. No recuerdo si este es el caso en Java, pero sé con certeza que está en C # MSDN: "Los operadores binarios | están predefinidos para los tipos integrales y bool. Para los tipos integrales, | calcula el OR bit a bit de sus operandos. Para bool operands, | calcula el OR lógico de sus operandos "
pwny
Después de un poco más de investigación, descubrí que la única diferencia entre | y || y & y && para operandos booleanos en Java es el comportamiento de corto circuito, por lo que el escenario que está describiendo no es realmente posible.
pwny
0

Un ejemplo más simple de error:

int condA=1, condB=2;

if (condA!=0 && condB!=0) {
    // correct!
}
if ((condA & condB)!=0) {
    // never executed
}

aquí tienes dos condiciones, ambas son distintas de cero; pero el bit a bit &resulta en cero.

Javier
fuente
@Javier: Gracias por responder, pero estoy un poco confundido. Estoy trabajando en Java, y este código no se compila. (condA && condB)errores, porque && no funciona durante 2 ints, solo 2 booleanos. (condA & condB)si bien es correcto, se evalúa como an int y en java no podemos decirlo, if(int)por lo que también es un error. Sin embargo, eres el primero en entender lo que estoy buscando, exactamente ese ejemplo de error .
Deerasha
no he usado Java en mucho tiempo ... intente (Boolean(condA) && Boolean(condB)) (creo que Boolean(x)es truepara enteros distintos de cero, ¿verdad?)
Javier
No @Javier: No se puede transmitir de int a boolean.
Deerasha
¿qué pasa ((condA!=0) && (condB!=0))?
Javier
@Javier yay compila, pero el "error" ya no se ilustra if (((condA!=0) && (condB!=0))) { System.out.println("correct"); } if (((condA!=0) & (condB!=0))) { System.out.println("never executed?"); }ejecuta ambas declaraciones de impresión.
Deerasha