04 January 2023

The danger of And and Or

The previous blogpost discussed the difference between And vs &&, and Or vs || in conditional statements. It showed why && and || should be used instead of And and Or. The post also contained an example where it is legitimate to use the And operator:

Local Int x = %111      ' binary notation of 7
If x And %11 > 0        ' test if lower 2 bits are set
  ' do something
EndIf

However, this condition is true for the wrong reasons. The condition is also true when only bit one is set. To prevent this and only check for the two lower bits we could change it into:

If x And %11 == 3       ' test if lower 2 bits are set

This seem to work ok, but the condition is still true for the wrong reason and that's gone haunt you. Let's try something different, let's test if bit 1 (value is 2) is set using the same method:

If x And %11 == 2       ' test if bit 1 is set
  Message "Condition is True"
Else
  Message "Condition is False"
EndIf

When we run this code, the program displays:

Screenshot 2023-01-04 071908

As you can see, the statement does not recognize that bit 1 is set (binary %10 == 2), despite the fact that the bit is 1.

So, what is going on here? Can't we trust And anymore? Maybe it is a bug? It is not a bug; it works like this is in any language and to understand this behavior we need to look at the operator precedence table. The helpfile contains the table under the topic name Operator Hierarchy:

( ) parenthesis
+ - ~ ! unary plus, unary minus, bitwise NOT, logical NOT
$ & explicit string addition
^ the power of
* / multiply, divide (floating-point)
\ Div Mul integer division, integer multiplication
% Mod Fmod integer and the floating point modulo
+ - Add Sub addition (the string addition, too) and subtraction
<< >> Shl Shr Rol Ror all shift and rotate operators (also: Shl%, Rol|, Sar8, etc.)
%& bitwise And
%| the bitwise Or
= == < > <= >= != all comparisons (also: NEAR ...)
And bitwise And
Or bitwise Or
Xor Imp Eqv bitwise exclusive Or, implication and equivalence
&& logical And
|| logical Or
^^ logical exclusive Or
Not bitwise complement

When an expression is evaluated the rules of operator precedence are applied. For instance, multiplication and division go before addition and subtraction. The table shows this by placing the * and / operators before (or above) + and -. Now look at the And operator; it comes after the comparison operators. This means that if an expression contains a comparison operator, it is evaluated (computed/calculated) before the And operator is applied. In our case, the part %11 == 2 is evaluated before And is applied. Since %11 = 3, 3 is not equal to 2 (results in False) and x And 0 is always 0, the result is 0 (False). These are the steps involved in the evaluation:

(Step 1) If x And %11 == 2 
(Step 2) If x And False
(Step 3) If x And 0
(Step 4) If 0

Can you now see why the original example form above results in True?

If x And %11 == 3       ' True for the wrong reason

The problem can be solved by using either parenthesis or %&:

If (x And %11) == 2     ' test if bit 1 is set
If x %& %11 == 2        ' GB way

In all other languages than GB the first solution is used. However GB defines an additional 'And' operator with a higher precedence that comes before the comparison operators: the %& operator. See the table for its location.

The Or operator suffers from the same problem, so GB defines an or-operator with higher precedence, the %| operator.

Is there a moral to this story? Try to avoid the use of And (&) and Or (|), unless - for instance - when translating C/C++ code to GB. (C/C++ does not support %& and %|.) Otherwise you might prefer the use of %& and %|.

No comments:

Post a Comment