![]() |
The Best-Kept Secret of Programming |
Note: the following, although presented in old BASIC format,
is applicable to Visual Basic and spreadsheets as well. These methods are particularly
useful in streamlining PowerBasic macros.
The GW-BASIC User's Guide and Reference, Microsoft, 1989, contains a small section explaining the function of relational operators. The text begins:
"Relational operators let you compare two values. The result of the comparison is either true (-1) or false (0). This result can then be used to make a decision regarding program flow."
The GW-BASIC Reference, McGraw-Hill, 1990, features a similar section with this sentence:
"When two values are compared, the relational operators return a result of -1 (true) or 0 (false)."
The GW-BASIC 3.20, Epson, 1986, is similarly vague:
"A logical operation uses Boolean mathematics to define a logical
connection between the results
Those manuals make no further reference to the numeric value of logical expressions, except in regards to the use of the bitwise AND/OR, which really is a different construct. Also, despite extensive searching, I have found no other web page that addresses this issue, per se.
"Big deal," you might say. Computers work with numbers.
Every relational expression, such as
Before going further, perhaps you would like to indulge in a little experiment.
Run Basic At the prompt, enter the statement,
It seems that relational expressions need not exist only within an IF statement or as a condition of a WHILE loop, yet those are the only constructs in which one ever sees them being used. Which brings us to The Great Unknown Of (Basic) Programming, and you may have heard it here first:
The relational and logical operators can be used in ANY mathematical context!
A corollary to that is:
Any LOGICAL CONDITION can be expressed as an ALGEBRAIC EQUATION
That means formulas. (It also happens to mean that the keywords IF and THEN are technically redundant, and that any program can be written without them! But that's the stuff of another article, and those keywords do make life easier — more about that later.) Let us see some examples, which I have termed "Booleans" for lack of a more imaginative designation.
40 X=2: IF Y<>0 THEN X=3
can be written as:
40 X=2 -(Y<>0)
The term (Y<>0) is evaluated. If true, it is assigned
a value of -1. The expression becomes
A multiplier can be utilized to effect a specific change in value:
180 IF A=B THEN A=A-4
can be shortened to:
180 A = A -4*-(A=B)
which might as well be written as:
180 A = A +4*(A=B)
4*(-1) is subtracted from A, if A entered the equation equal to B.
Logical operators may be included in the mix:
230 IF P=5 OR P=6 THEN Q=Q*2
becomes:
230 Q = Q -Q*(P=5 OR P=6)
This also could be written as:
230 Q = Q -Q*((P=5)+(P=6))
The logical OR is equivalent to arithmetic
ADDITION.
370 IF R>0 AND S=7 THEN T=5 ELSE T=2
could be written as either:
370 T = 2 -3*(R>0 AND S=7)
or:
370 T = 2 +3*(R>0)*(S=7)
The logical AND equates to arithmetic
MULTIPLICATION. Notice the difference in the sign in front
of the 3. In the first case, there is but one Boolean term,
which will evaluate to
One might be able to combine several statements, and string comparisons are fair game:
230 IF D$=E$ THEN J=J-1
232 IF F<=3 THEN J=J+2
can become this:
230 J=J +(D$=E$) -2*(F<=3)
Coding can be particularly concise when zeroes are involved:
380 D=0: IF A<>0 AND B<>0 AND C<>0 THEN D=8
can be reduced to:
380 D = -8*(A*B*C<>0)
D goes from zero to 8 only if A, B, and C all have value. Now that's pretty.
Nested conditional statements can be assimilated:
230 IF R<0 THEN Q=Q+1: IF S=0 THEN Q=Q+1
equates to:
230 Q = Q+(R<0)*(1-(S=0))
Nothing happens unless R<0, whereupon Q is incremented by 1 or 2, depending upon the value of S.
These code samples are well and good, and one can readily see that Booleans streamline
code in fine style, but might they serve a grander purpose? Are there any truly useful
applications for this stuff? The answer is an unequivocal "Yes."
Let's look at a good example.
HANDLING LETTER CASE
10 '----------[ convert 1 character to uppercase ]----------
11 '
12 DEF FNUP$(C$) = CHR$(ASC(C$) -32 * (ASC(C$)>96) * (ASC(C$)<123))
The lowercase letters are Ascii 97-122; uppercase letters are Ascii 65-91, or 32
less. If C$ is in the range of Ascii
This function would simplify the code to convert a string of text:
800 '-----------[ SUB: convert A$ to uppercase ]---------------
801 '
810 FOR J=1 TO LEN(A$): MID$(A$,J,1) = FNUP$(MID$(A$,J,1):
NEXT: RETURN
If desired, one could convert input to lower case by changing the formula's numbers to
Granted, in this context FNUP$ doesn't do anything that cannot be replicated by traditional methods, but the code is much cleaner. This function really shines is in the simplification of INPUT routines. Say that the user is to be prompted for a response to a Yes/No question. A portion of the subroutine might look like this:
470 A$ = INPUT$(1)
480 IF A$="Y" OR A$="y" THEN
{do this}
490 IF A$="N" OR A$="n" THEN
{do that}
Repetitive double-trapping of various alpha characters throughout a program can become quite tedious, but now those days are gone, for henceforth you will code thusly:
470 A$ = FNUP$(INPUT$(1))
480 IF A$="Y" THEN {do this}
490 IF A$="N" THEN {do that}
This next example combines FNUP$ with another nifty Boolean shortcut. Let's say that a user is asked to select a letter option from a menu. Program control would branch, depending upon the response.
---- MENU ----
(M) morning
(A) afternoon
(E) evening
(L) late
500 PRINT "Select a time:";
510 A$ = FNUP$(INPUT$(1))
520 ON -(A$="M") -2*(A$="A") -3*(A$="E")-
4*(A$="L") GOTO 1000, 2000, 3000, 4000
530 BEEP: GOTO 510
This routine accepts only a proper response and directs control accordingly, obviating several lines of traditional code in the process.
CALENDAR STUFF
A leap year is any year evenly divisible by 4, excepting the century years not divisible by 400. So 2000 is a leap year, but 1900 and 2100 are not.
A reasonable subroutine for making such a determination might look like this:
430 LEAPYR = 0: IF YR MOD 4=0 THEN LEAPYR = 1
440 IF (YR MOD 100=0) AND (YR MOD 400<>0) THEN
LEAPYR = 0
Compare that with the Boolean equivalent:
430 LEAPYR = (YR MOD 4=0)*((YR MOD 100<>0) + (YR MOD 400=0))
Both routines return 1=yes, 0=no.
To access the number of days in a given month, one has options. For purposes of
this example, M is the month in question, and there are no leap-year adjustments:
70 DIM MO.DAYS(12)
80 FOR J=1 TO 12: READ MO.DAYS(J): NEXT
90 DATA 31,28,31,30,31,30,31,31,30,31,30,31
Alternatively, one could choose to eliminate the array by setting a variable (or function) thusly:
MO.DAYS = 31 +(M=4 OR M=6 OR M=9 OR M=11) +3*(M=2)
This also could be coded as:
MO.DAYS = 31 +(M=4) +(M=6) +(M=9) +(M=11) +3*(M=2)
A discussion of Booleans is not complete without an acknowledgement of the downside. In fact, Booleans run slower than IF..THEN statements. Convenient though they may be, math calculations require more CPU time than logic functions. In most programming situations, the time differential is unnoticeable; however, the programmer should avoid the use of Booleans in multiple-loop structures where speed is critical.
Also, don't plan on having a colleague proofread your code, unless he/she also has been assimilated into the Boolean fold!
To close out this section, I would like to share one last tricky function call, which won me a monthly prize from a trade magazine. C-64 Basic had nothing resembling a 'PRINT USING' statement (the entire operating system was only 8k in size!), so data had to be organized for printout in some other way. I doubt whether you are still programming a Commodore, but if you simply are weary of formatting PRINT USING statements, you might enjoy this function that aligns numerical input around the decimal point, irrespective of the size of the numbers:
30 DEF FNUSING(X) = INT(LOG(ABS(X)-(ABS(X)<1))/LOG(10)) +(ABS(X)<1) -(X=0 OR X=1000)
This scary-looking formula merely determines the number of characters that are to the left of the decimal point, by exploiting the idiosyncrasies of the LOG function. Example usage is as follows:
660 PRINT TAB(20 -FNUSING(NUM)); NUM
Data values are limited to less than 1 million, due to an anomaly regarding the powers of 1000; larger numbers could be accommodated by augmenting the last term to:
... -(X=0 OR X=1000 OR X=1000000)
In this case, X would have to be defined as double-precision as well.
MANY PROGRAMMERS STILL DON'T GET IT
I tried the command PRINT 2=2 to begin my first-ever session on a Commodore-64, just to see what would happen — thereby discovering the most interesting feature of Basic on my first day as a programmer. I shared this find with my like-minded brother, who now uses Booleans religiously in his own programs.
On the other hand, discussions with several university math instructors didn't fare as well. I was amazed by the nearly universal ignorance of the true capabilities of Boolean math, even though equivalent constructs are valid in certain other programming languages and on any spreadsheet (but be aware that they mostly use True = +1)! One professor even claimed that "That isn't Basic," but his face-saving edict was in error. If the interpreter/compiler doesn't complain, then the code is valid by default, whether in Basic or any other language.
Actually, there are other folks familiar with Booleans —
or at least, there were. For Commodore-64 users, every byte of ram was so precious
that no spaces were required in program code!
As they saved a lot of bytes, Boolean constructs were discussed regularly in the
On the modern front, I recently viewed a German web page detailing some tricks for speeding up Visual Basic programs. It correctly suggested that someone might code a simple function in this manner:
Private Function Comp(a$, b$) as Boolean
If a$ > b$ Then Comp = True Else Comp = False
End Function
The article pointed out that not only is the ELSE clause redundant, but a simpler, faster construct is available:
Private Function Comp(a$, b$) as Boolean
Comp = a$ > b$
End Function
The difference between those two calls is analogous to our GW-Basic study:
70 IF A$ > B$ THEN COMP = 1 ELSE COMP = 0
as compared to the Boolean equivalent:
70 COMP = -(A$>B$)
Another cited VB shortcut is an inherently Boolean concept. The following statement is redundant, yet one sees this sort of code all too frequently:
IF Z<>0 THEN ... {so and so}
A numeric variable equal to zero is logically False. That is the definition of False — having no value. The same applies to string variables: a null string is False. In all cases, a value of any kind renders a variable True by default. Therefore,
IF Z THEN ... {this and that}
is perfectly valid, and it runs faster to boot. Variable Z is evaluated; if it exists (has a value), then it is True, and the next portion of the statement is executed. The interpreter/compiler knows the rules of symbolic logic; it doesn't need to be reminded that something with a value is not equal to zero. 

The primary point is this: the fact that such instructional pages exist indicates that even modern-day programmers are largely ignorant of the Boolean Mystique. Perhaps it is yet another federal conspiracy, or perhaps programmers in general need to have their auras recharged.
I hope that you have enjoyed this introduction to the mysteriously beautiful, yet highly efficient world of Boolean Basic. Feedback is welcome.
Computer programming
GW-Basic
BASIC programming shortcuts
BASIC function calls
Boolean logic arithmetic mathematics
logical operator
relational operator
PowerBasic