Using Logical Operators with Non-Boolean Expressions

If you have been programming Visual Basic for any length of time,
you have probably gotten yourself into trouble using VB’s logical operators with non-Boolean values.
The most commonly used (and misused) operators are Not, and And.
Virtually any program you write will make use of one of these operators somewhere in the code,
and although they are conceptually quite simple, they can be the source of elusive bugs.

As long as you restrict your use of logical operators to Boolean variables and expressions,
your logic will behave predictably and reliably.
However, the Visual Basic documentation states that any non-zero value evaluates to True in an If statement,
so you may be tempted to write code that looks something like this:

' Listing 1:
Dim intI As Integer
intI = 1
If intI Then
   MsgBox "intI is true"
Else
   MsgBox "intI is false"
End If

If you run this code, the If statement will behave exactly as you expect
and the program will display a message box that says "intI is true."
However, consider the following code:

' Listing 2:
Dim intI As Integer
intI = 1
If Not intI Then
   MsgBox "intI is false"
Else
   MsgBox "intI is true"
End If

This time the message box displays "intI is false," which is probably not at all what you expected.

The And operator has similar problems, as the following code demonstrates:

' Listing 3:
Dim intI As Integer
Dim intJ As Integer
intI = 1
intJ = 2
If intI And intJ Then
   MsgBox "the expression is true"
Else
   MsgBox "the expression is false"
End If

When you run this code, you get a message box that says "the expression is false."
If that is not the result you are expecting, then imagine trying to find this bug in code that is buried deep within your program.

Logical Operators are Really Bitwise Operators

So how do you avoid these problems? Well, it helps to understand exactly what is happening in each of these examples.
The first thing you must understand is that VB does not really have genuine logical operators.
VB logical operators are actually bitwise operators,
which perform logical operations on your variables and expressions at the bit level.

To understand bitwise operations, you need to know how variables are stored in memory.
Ultimately, all data (regardless of type) is represented in memory by a pattern of bits that each have a value of 1 or 0.
Bits are typically organized into bytes, each of which contains 8 bits.
The example code utilized Integer variables, which use two bytes (or 16 bits) of memory to hold their value.
The bit pattern for a two-byte integer value of 1 looks like this:

00000000 00000001

The Not Operator

The Not operator negates all of the bits, which means it flips 1’s to 0 and 0’s to 1.
So using our same example, using the Not operator on an integer with a value of 1 produces the following:

11111111 11111110

At this point, a light may have gone on in your head.
If you had to guess at the bit pattern for zero, you would probably guess that it is 16 bits all set to 0, and you would be right.
However, using Not with a value of 1 clearly does not produce a bit pattern of all zeroes.
Consequently, the If statement in listing 2 produces "x is false" because the expression "Not 1" is a non-zero value,
and any non-zero value is considered True by the If statement.

The And Operator

So what about And? The And operator is a bitwise operator too.
The job of And is to compare the corresponding bits of each expression
and produce a new bit pattern that contains a 1 everywhere both expressions had a 1 and a 0 where either expression had a 0.
The two integer variables from our code example have the following bit patterns:

00000000 00000001 (intI = 1)
00000000 00000010 (intJ = 2)
-------- --------
00000000 00000000 (intI And intJ)

When you use And on these two bit patterns,
you end up with a result that is all zeroes because there aren’t any 1 bits in intI that correspond with 1 bits in intJ.
So the expression "intI And intJ" in listing 3 turns out to be false, even though both intI and intJ are non-zero values.

False vs. True

If the prior discussion made any sense to you,
then you can probably guess why the VB built-in constant for False is 0 and True is -1.
A boolean variable is basically an integer that contains either 0 or -1.
The bit pattern for -1 is:

11111111 11111111

When you use the Not operator on the value True, you get False and vice-versa.
Boolean variables only contain either True or False, which is why using VB’s logical operators is only safe with variables declared as type Boolean.

Be True to Your Boolean Expressions

It is unfortunate that VB does not provide different operators for bitwise operations versus logical operations as other languages do.
To avoid bugs, you should be careful to explicitly or implicitly use boolean expressions when you need to perform a logical comparison.

You create explicit boolean expressions by declaring variables as Boolean, or by using the Cbool function to convert non-boolean values.
You can create an implicit boolean expression by performing a relational comparison, as the following code demonstrates:

' Listing 4:
Dim intI As Integer
Dim intJ As Integer
intI = 1
intJ = 2
If (intI <> 0) And (intJ <> 0) Then
   MsgBox "the expression is true"
Else
   MsgBox "the expression is false"
End If

In listing 4, the "<>" (not equal to) relational operator compares intI and intJ to 0,
implicitly producing two boolean results. The And operator then works as expected.

In case you are wondering, the Or operator does not generally cause problems like Not and And do.
It too is a bitwise operator, but using Or on two non-zero expressions always results in a non-zero value
and using it on two zero expressions always results in zero, so your conditional statements behave as expected.

Conclusion

Now you know why it is best to use only Boolean variables and expressions with Not and And in any logical comparison.
You will avoid creating bugs that are difficult to find.
If you must use non-boolean variables, keep the bitwise nature of these operators in mind so you can accurately predict how your code will work.