Quando é apropriado usar um operador bit a bit em uma expressão condicional?

15

Primeiro, alguns antecedentes: sou professor de treinamento em TI e estou tentando introduzir os operadores booleanos de java na minha turma da 10ª série. Meu professor-mentor examinou uma planilha que eu preparei e comentei que poderia deixá-los usar apenas uma única & ou | denotar os operadores, porque "fazem a mesma coisa".

Estou ciente da diferença entre & e &&.
& é um operador bit a bit destinado ao uso entre números inteiros, para executar "ajustes de bits".
&& é um operador condicional destinado ao uso entre valores booleanos.

Para provar que esses operadores nem sempre "fazem a mesma coisa", propus-me a encontrar um exemplo em que o uso do bit a bit entre valores booleanos levaria a um erro. Eu encontrei este exemplo

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 exemplo mostra que, se um valor tiver que ser alterado na segunda metade da expressão, isso levaria a uma diferença entre os resultados, já que o bit a bit é um operador ansioso, enquanto o condicional se comporta como um curto-circuito (não avalia o segundo metade, se a primeira metade for falsa no caso de && e verdadeira no caso de ||).

Eu tenho um problema com este exemplo. Por que você deseja alterar um valor ao mesmo tempo em que faz uma comparação? Não parece uma maneira robusta de codificar. Eu sempre fui avesso a fazer várias operações em uma única linha no meu código de produção. Parece algo que um "cowboy de codificação" sem consciência quanto à manutenção de seu código faria. Eu sei que em alguns domínios o código precisa ser o mais compacto possível, mas certamente essa é uma prática ruim em geral?

Eu posso explicar minha escolha de incentivar o uso de && e || sobre & e | porque esta é uma convenção de codificação aceita em engenharia de software .

Mas alguém poderia me dar um exemplo melhor, até do mundo real, do uso de um operador bit a bit em uma expressão condicional?

Deerasha
fonte

Respostas:

16

é apropriado quando você está executando uma operação de mascaramento

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

onde aeb são números inteiros

|| e | e && e & não são intercambiáveis

| e & quase nunca aparecerão em uma expressão condicional por si mesmos (o ponto do link que você incluiu é que essas coisas geralmente são erros)

EDIT: Não discuta com seu mentor; mesmo se você ganhar, você perde. Em vez disso, explique que você não deseja confundir os alunos misturando operadores lógicos e operadores bit a bit na mesma lição. Você poderia explicar que, se i = 3 ej = 2, então i & j = 2, enquanto i && j é um erro. No entanto, a explicação mais simples é que você está ensinando operadores booleanos (lógicos), portanto, jogar equivalentes bit a bit em casos especiais é uma distração do ponto principal da lição. Não há necessidade de tornar o mentor "errado", nem de produzir contra-exemplos. O foco da lição está nos operadores booleanos, não nos operadores bit a bit.

Como corolário, quando você começa a ensinar aos operadores bit a bit, não há necessidade de mostrar os casos especiais em que + e - produzem os mesmos resultados que & e |

Steven A. Lowe
fonte
O código acima não está errado, porém, pode ser confuso, mas, como está, o código não contém um erro, não é? Eu concordo que o uso de operadores bit a bit dessa maneira não é muito legível, eu não gostaria que o visse em uma revisão de código, mas é Java legal (e C # também, ignorando o System.out.println).
Steve
Obrigado por responder @Steven A. Lowe. Você disse: "|| e | e && e & não são intercambiáveis", mas bitwise = !(true & true == false);e condition = !(true && true == false);ambos irão avaliar a verdade, portanto, neste caso, eles são intercambiáveis? Sintaticamente, talvez, já que o código ainda é compilado. Concordo que eles são usados ​​para coisas diferentes semanticamente, como mencionei no parágrafo 2. Você diz isso! e & "quase nunca aparecem condicionais por si mesmos". Estou procurando esses casos "quase nunca" e me perguntando se eles existem legitimamente.
precisa saber é o seguinte
@Deerasha: || e && operam apenas em booleanos. & e | operar em números inteiros e booleanos - há pouco sentido em usar operadores bit a bit (destinados a operar em vários bits ) para manipular booleanos, e fazê-lo com abandono pode levar a códigos confusos e comportamentos inesperados (por exemplo, se você acidentalmente usar um número inteiro em vez de um booleano, os operadores de bits não se queixam)
Steven A. Lowe
Sim @Steve Haigh, o compilador não o rejeita, mas não é o caminho certo para usá-los, a julgar pelo padrão de codificação publicado ao qual vinculei. Posso descansar confortavelmente em descartá-lo porque ele não está em conformidade com um padrão de codificação ou o java talvez indique que esse é um uso inadequado?
precisa saber é o seguinte
2
@ Steven A. Lowe: se i = 3 ej = 2, então i & j = 2, enquanto i && j é um erro Isso é brilhante! É simples e faz o ponto. No nível da 10ª série, esse também é um erro provável de cometer, pois eles ainda estão se acostumando ao tipo booleano e ao que os operadores se aplicam. Muito obrigado! Grande conselho sobre não discutir com meu mentor também.
precisa saber é o seguinte
12

Os operadores não bit a bit &&e ||são operadores de curto-circuito. Em outras palavras, com &&, se o LHS for falso, o RHS nunca será avaliado; com ||se o LHS for verdadeiro, o RHS nunca será avaliado. Por outro lado, os operadores bit a bit &e |são sem curto-circuito e sempre avaliarão o LHS e o RHS. Caso contrário, eles são equivalentes em umif declaração.

A única vez em que vejo valor ao usar operadores sem curto-circuito é se o RHS tiver algum tipo de efeito colateral desejável que você deseja que aconteça em todos os casos. Não consigo pensar em um exemplo específico em que você queira isso e não acredito que seja uma boa prática, mas essa é a diferença.

Jonathan
fonte
1
+1. Exatamente correto, ao comparar operadores booleanos bit a bit e lógicos sempre retornam o mesmo resultado, mas (em Java e C # pelo menos) apenas os operadores lógicos causam um curto-circuito.
Steve
Obrigado por responder. Seu parágrafo 1 foi o que eu estava tentando obter no meu parágrafo 4, afirmando que bit a bit é um operador ansioso enquanto o condicional se comporta como um curto-circuito . Seu parágrafo 2 é a preocupação que descrevi no parágrafo 5. Portanto, estou ciente da diferença, mas estou procurando esse exemplo específico que nem você nem eu podemos pensar.
Deerasha
7

A resposta filosófica geral é que o uso de operadores bit a bit para operadores booleanos é atípico e dificulta a leitura do código. Na prática (para código em produção), um código mais legível é mais fácil de manter e, portanto, mais desejável.

Para uma utilização real da necessidade de operadores de curto-circuito, consulte casos como:

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

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

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

Esse tipo de operação aparece frequentemente no código do mundo real.

Kathy Van Stone
fonte
Tfa. Como explico aos meus 15 anos que 1 &é "mais difícil de ler" que 2? Não tenho um exemplo no qual os 2 operadores não funcionem da mesma maneira para operandos booleanos. Concordo com seu ponto de vista sobre um código mais legível e mais fácil de manter. Quero encorajá-los a escrever um código bonito. Mas ter alguma prova na minha bolsa de ferramentas seria mais convincente do que "porque eu disse isso". Eu tenho isso declarado como padrão nesse link, e pode ter que confiar apenas nisso, se eu não conseguir o exemplo que estou procurando. Como perguntei a Steve Haigh: o java deveria indicar isso como um uso inadequado?
Deerasha
@ Deerasha Eu estava pensando mais em uma discussão com seu mentor. Para a classe, nem tente dizer a eles que você pode usar operadores bit a bit para condições lógicas.
Kathy Van Stone
4

Você usará operadores bit a bit se comparar as enumerações de Bitmask. Por exemplo, você tem uma enumeração de estados e um objeto que pode estar em mais de um desses estados. Nesse caso, você fará um bit a bit ou para atribuir mais de um estado ao seu objeto.

por exemplo, state = CONNECTED | IN_PROGRESSonde CONNECTED could be 0x00000001eIN_PROGRESS 0x00000010

Para obter mais informações, consulte a documentação de enumerações de sinalizadores.

pwny
fonte
Graças a você, aprendi uma aplicação de operadores bit a bit que eu não conhecia antes! Mas neste exemplo, apenas o operador bit a bit se aplica. Estou procurando por um pedaço de código bem escrito em que o uso do bit a bit em vez do condicional levaria à compilação, mas uma saída incorreta. Ou seja, se esse fragmento de código existir.
Deerasha
Eu não acho que isso possa acontecer em Java, como acredito que chamar o | ou & operadores em valores que não podem ser bit a bit e que ou ou que apenas executam uma lógica | ou &. Não me lembro se esse é o caso em Java, mas sei com certeza que está no C # MSDN: "| operadores binários são predefinidos para os tipos integrais e bool. Para tipos integrais, | calcula o OR bit a bit de seus operandos. Para bool operandos, | calcula o OR lógico de seus operandos"
pwny
Depois de mais algumas pesquisas, descobri que a única diferença entre | e || e & e && para operandos booleanos em Java é o comportamento de curto-circuito, portanto o cenário que você está descrevendo não é realmente possível.
Pwny #
0

um exemplo mais simples de erro:

int condA=1, condB=2;

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

aqui você tem duas condições, ambas são diferentes de zero; mas o bit a bit &resulta em zero.

Javier
fonte
@Javier: Obrigado por responder, mas estou um pouco confuso. Estou trabalhando em java, e esse código não compila. (condA && condB)erros, porque o && não funciona por 2 ints, apenas 2 booleanos. (condA & condB)enquanto correto, avalia para um int e em java, não podemos dizer if(int)que também erros. Você é o primeiro a entender o que estou procurando - exatamente esse exemplo de erro .
Deerasha
não usei Java em um longo tempo ... tentar (Boolean(condA) && Boolean(condB)) (eu acho que Boolean(x)é truepara não-zero de números inteiros, né?)
Javier
Não. @Javier: Não é possível transmitir de int para booleano.
Deerasha
que tal ((condA!=0) && (condB!=0))?
Javier
@Javier yay ele compila, mas o "erro" não é mais ilustrado e if (((condA!=0) && (condB!=0))) { System.out.println("correct"); } if (((condA!=0) & (condB!=0))) { System.out.println("never executed?"); }executa ambas as instruções de impressão.
Deerasha