Boolean nem true nem false

Delphi

18/04/2005

Delphi 6
var a,b:Boolean; Baiti:Byte;
begin
  a     := true;
  Baiti := 2;
  Move(Baiti, b, 1);
  if a and b then
    ShowMessage(´b é true´)
  else
  if a and not b then
    ShowMessage(´b é false´)
  else
  if a then
    ShowMessage(´b é ?´);
end;
Alguém poderia explicar ?


Kapak

Kapak

Curtidas 0

Respostas

Beppe

Beppe

18/04/2005

Caso 1) O Delphi trata 0 como False e 1 como True.
Caso 2) Em certas ocasiões, trata qualquer valor diferente de 0 como True.

Mas no caso dos operadores [b:df697bf3a3]and[/b:df697bf3a3], [b:df697bf3a3]or[/b:df697bf3a3] e [b:df697bf3a3]xor[/b:df697bf3a3], o procedimento é o mesmo dos inteiros. E sem olhar o código gerado, pode ser um tanto difícil adivinhar o que será feito.

O [b:df697bf3a3]not [/b:df697bf3a3]tem um tratamento especial em alguns casos. Dentro de um [b:df697bf3a3]if [/b:df697bf3a3]é o Caso 2. Numa atribuição, é o Caso 1.

Atribuição simples:
a and b = 01b and 10b = 0 = False
not b = 10b xor 01b = 11b = True

Teste if ou atribuição complexa:
a and b = 01b and 10b = 0 = False
a and not b = (01b <> 00b) and not (10b <> 00b) = 01b and not 01b = 01b and 00b = 00b = True

O problema é o Move. Use b := Baiti <> 0;

PS: o sufixo b é de binário


GOSTEI 0
Massuda

Massuda

18/04/2005

Internamente o tipo de dado Boolean é assumido como sendo uma enumeração do tipo
type
  Boolean = (False, True);

Quando você faz...
  Baiti := 2; 
  Move(Baiti, b, 1);
  // ou simplesmente
  // b = Boolean(2)
...você está atribuindo um valor fora de faixa à variável b. A rigor, isso já é um erro.

Delphi avalia uma variável Boolean no estilo da linguagem C, ou seja, falso é ZERO e verdadeiro é NÃO-ZERO; assim
b := Boolean(2);

if b then
  {sempre executado}
else
  {nunca executado}

O problema são os operadores lógicos...

Os operadores lógicos AND, OR e XOR tratam os booleans como inteiros, de modo que o AND, OR e XOR são feitos bit-a-bit.
a := True;
b := Boolean(2);

if b then 
  {sempre executado}
else
  {nunca executado}

if a and b then // = 1 and 2 = 0
  {nunca executado} 
else
  {sempre executado}

O operador lógico NOT é implementado como sendo:
Not X = Boolean(Byte(X) Xor Ord(True))
de modo que
b := Boolean(8);
b := Not b;
// Byte(b) = 9


No caso, ao avaliar a expressão lógica nos if´s, cada parte da expressão é avaliada conforme o operador lógico utilizado e no final verifica se o resultado é ou não zero.

Lembro que no Pascal original, True = 0 e False = -1 (ou o contrário, não lembro), mas isso foi alterado nas primeiras versões do Delphi (quando surgiu o C++ Builder, acho).


GOSTEI 0
Marco Salles

Marco Salles

18/04/2005

beep :
O problema é o Move.


Massuda :
você está atribuindo um valor fora de faixa à variável b. A rigor, isso já é um erro


Massuda :
Lembro que no Pascal original, True = 0 e False = -1 (ou o contrário, não lembro),



GOSTEI 0
Aroldo Zanela

Aroldo Zanela

18/04/2005

Colegas,


Syntax{$B+} or {$B-} {$BOOLEVAL ON} or {$BOOLEVAL OFF} Default{$B-} {$BOOLEVAL OFF} ScopeLocal Remarks The $B directive switches between the two different models of code generation for the and and or Boolean operators. In the {$B+} state, the compiler generates code for complete Boolean expression evaluation. This means that every operand of a Boolean expression built from the and and or operators is guaranteed to be evaluated, even when the result of the entire expression is already known. In the {$B-} state, the compiler generates code for short-circuit Boolean expression evaluation, which means that evaluation stops as soon as the result of the entire expression becomes evident in left to right order of evaluation. For further details, see the section ´Boolean operators´ in the Object Pascal Language Guide.



{$B+}

procedure TForm1.Button1Click(Sender: TObject);
var a:Boolean;
    b:Boolean;
    Baiti:Byte;
begin
  a     := true;
  Baiti := 2;
  Move(Baiti, b, SizeOf(Baiti));
  if a and b then
    ShowMessage(´b é true´)
  else
  if a and not b then
    ShowMessage(´b é false´)
  else
    ShowMessage(´b é ´+IntToStr(Integer(b)));
end;



GOSTEI 0
Kapak

Kapak

18/04/2005

Olá pessoal, obrigado pelas respostas apresentadas. Peguei este erro num programa que estava sujando a variável boleana. Confesso que ainda não entendí.
Mas no caso dos operadores and, or e xor, o procedimento é o mesmo dos inteiros.
Os operadores lógicos AND, OR e XOR tratam os booleans como inteiros, de modo que o AND, OR e XOR são feitos bit-a-bit.Código: a := True; b := Boolean(2); if b then {sempre executado} else {nunca executado} if a and b then // = 1 and 2 = 0
Mas se for feito:
{$B-}
if a and (b) then
  ShowMessage(´b é true´) // e é aqui que ele vem



GOSTEI 0
Kapak

Kapak

18/04/2005

Acho que ele deve tratar (b) como Boolean(b)


GOSTEI 0
Massuda

Massuda

18/04/2005

O que o Beppe e eu tentamos dizer é que quando uma expressão lógica envolve AND, OR e XOR, os booleans são tratados como inteiros. Isso significa que a expressão
if a and b then...
é na verdade vista pelo compilador como sendo
if Boolean(Integer(a) and Integer(b)) then...

Com relação a outra questão...
Mas se for feito:
{$B-}
if a and (b) then
  ShowMessage(´b é true´) // e é aqui que ele vem
O $B- faz com que o compilador tente simplificar as expressões lógicas.

No seu exemplo, a é sempre True, logo o compilador precisa apenas checar o valor de b, para saber se o AND é verdadeiro; como ele não precisa fazer o AND, ele apenas testa se B é não-zero, o que resulta em True.

Note que se o teste usasse OR ao invés de AND, o compilador nem precisava testar o valor de b, pois a já é True.


GOSTEI 0
Kapak

Kapak

18/04/2005

Ainda não entendí.
O $B- faz com que o compilador tente simplificar as expressões lógicas. No seu exemplo, a é sempre True, logo o compilador precisa apenas checar o valor de b, para saber se o AND é verdadeiro; como ele não precisa fazer o AND, ele apenas testa se B é não-zero, o que resulta em True.
$B-} 
if a and b then    // Resulta em false
if a and (b) then // Resulta em true
No que os parênteses influenciam ?


GOSTEI 0
Massuda

Massuda

18/04/2005

if a and b then    // Resulta em false
if a and (b) then // Resulta em true
No que os parênteses influenciam ?
Os parênteses fazem o compilador avaliar a expressão numa ordem diferente... isso faz o compilador gerar código diferente em cada caso.

No primeiro caso, o compilador faz o AND entre a e b e acabou.
if (Integer(a) and Integer(b)) <> 0 then...


No segundo caso, o compilador avalia cada lado do AND separadamente, gerando um código mais ou menos assim
if Integer(a) <> 0 then
  if Integer(b) <> 0 then...


Sinceramente, num primeiro momento eu acharia que é um bug do compilador, mas como a causa disso é um valor fora de faixa, eu acho aceitável que quem fez o compilador diga que isso é um comportamento indefinido.


GOSTEI 0
Kapak

Kapak

18/04/2005

Já acho que [b:67b9d8fc1a](b)[/b:67b9d8fc1a] num [b:67b9d8fc1a]and[/b:67b9d8fc1a] é tratado como Integer(Boolean(b)) e não Integer(b).


GOSTEI 0
Massuda

Massuda

18/04/2005

Já acho que [b:bb15f13d08](b)[/b:bb15f13d08] num [b:bb15f13d08]and[/b:bb15f13d08] é tratado como Integer(Boolean(b)) e não Integer(b).
Eu dei uma olhada no código gerado... o que o compilador gera é mais ou menos o que coloquei no meu post anterior.


GOSTEI 0
Kapak

Kapak

18/04/2005

if Integer(a) <> 0 then 
  if Integer(b) <> 0 then...
Mas aí ele faz um if encadeado, ou seja, insere uma instrução adicional e, consequentemente perde mais tempo.


GOSTEI 0
Anderson_blumenau

Anderson_blumenau

18/04/2005

Entao beppe isso significa q o compilador usa uma tabela logica pra achar os valores?

tipo

0 eh falso
1 eh verdadeiro


A and B

0  0 =  0 falso
1  0 =  0 falso
1  1 =  1 verdadeiro

Definicao: A saida eh verdade se somente todas as entradas forem verdade



GOSTEI 0
Beppe

Beppe

18/04/2005

Entao beppe isso significa q o compilador usa uma tabela logica pra achar os valores?

Isso em condições normais. No caso do [b:bedb633d4e]kapak[/b:bedb633d4e], o código que ele usa depende de como o compilador interpreta em cada caso. Reitero que iso não deve ser usado(Move´s e typecast´s), já que torna o código menos legível e difícil de manter.


GOSTEI 0
Kapak

Kapak

18/04/2005

Reitero que iso não deve ser usado(Move´s e typecast´s), já que torna o código menos legível e difícil de manter.
Num caso por ex de vc montar um buffer de dados p/ serem enviados via Sockets. Eu utilizo ReallocMem e Move p/ depois dar um SendBuf c/ este ponteiro.
Como vc faria ?


GOSTEI 0
Beppe

Beppe

18/04/2005

Prefira registros:

type
  TRec = record
    B: Boolean;
    I: Integer;
    D: Double;
    S: ShortString; // = String[255]
  end;

var
  R: TRec;
begin
  R.B := True;
  ...
  Socket.SendBuf(@R, SizeOf(TRec));
end;

Isto resolve o problema na maior parte dos casos. Em outros casos, pode usar ponteiro para um record, neste caso dá pra abrigar um array de tamanho ´flexível´.


GOSTEI 0
Kapak

Kapak

18/04/2005

Olá Beppe, conheço bem registros e os utilizo com bastante frequência.
No meu caso, leio um cursor de dados de um Dataset e tenho que enviá-los via Sockets p/ a aplicação cliente; pode vir qualquer tipo de dado inclusive blobs; é por isso que utilizo ReallocMem e Move(que na verdade não é um move e sim um copy). Se vc já ouviu falar num objeto chamado RPCBuffer, pode ver que ele utiliza a mesma lógica p/ alocação de memória e, eu como tive que escrever uma classe semelhante pq esta não implementa Sockets e nem consegue atualizar os dados já inseridos, faço alocação de memória e manipulação de dados com estes comandos.
É obvio que quando vc pode facilitar leitura de código, vc não vai complicar; mas tem casos que não dá.


GOSTEI 0
Beppe

Beppe

18/04/2005

Então acho que vc se encaixa na segunda situação. Pode usar ReallocMem, mas creio que ainda pode evitar os Move´s, a não ser que são inúmeras os registros que vc teria que criar.

var
  P: Pointer;
begin
  // aloque memória para P
  with TRec(P^) do
  begin
    B := True;
    S := ´Olá´;
  end;
  Socket.SendBuf(P, SizeOf(TRec));
end;


É isso que vc pretende?


GOSTEI 0
Kapak

Kapak

18/04/2005

Esse objeto já está criado e funcionando; ele cria uma tabela bidimensional(registros/campos) em memória e vc navega como num Dataset. Trabalha c/ ReallocMem e Moves pq não são só tipos de dados simples, mas também arrays simples, arrays de type records, lógica de Treeviews e blobs.


GOSTEI 0
POSTAR