Numeric Equates

PowerBASIC allows you to refer to numeric constants by name.  Be aware that equates have global scope; that is, they are visible throughout your program.  Unlike variables, you can use an equate on the left side of an assignment statement only once, and only a constant value (or a simple constant/literal expression) may be assigned to it.  If an expression is used, all parts of the expression must consist of constants, numeric equates; bitwise operators like AND, OR; and NOT; the arithmetic operators +, -, *, /, and \, and the relational operators >, <, >=, <=, <>, =; and the CVQ function.  For example, the following are all legal equate definitions:

%X = 1

%Y = 1 + 1

%Z = %X * %Y

%Q = (1& OR 2&) + (NOT 0)

%R = (%Q <> 100&)

%S = CVQ("DemoOnly")

A value must be assigned to each equate before it is referenced, even if that value is zero.  If you fail to define an equate, an error will be generated during compilation.  Numeric equates must be created outside of any SUB, FUNCTION, METHOD, or PROPERTY.  All equates are global, and may be referenced anywhere in the module.  For readability, we suggest placing equates at the top of your code.

A numeric equate name must always begin with a leading percent sign (%) and a letter (A-Z).  This is optionally followed by any combination of letters (A-Z), numbers (0-9), and underscores (_).  Equates created within an ENUM structure may also contain one period (.), which is inserted by the compiler as a delimiter.  All other characters are illegal.

If you are using a version of PowerBASIC which creates COM servers, you can easily include numeric equates in your type library; just append the words AS COM to the equate definitions:

%SCROLL_FLAG = 99 AS COM

You can also use equates to reduce the incidence of "magic numbers" in your programs.  Magic numbers are mysterious values that mean something to you when you first write a program, but not when you come back to it six months later.  Equates are particularly well suited for making programs more readable.  For example, consider an array to track chess pieces.  If we define:

%MAXPIECES = 32
%NPARAM    = 3

%NTYPE     = 1

%RANK      = 2

%FILE      = 3

%KING      = 1

%PAWN      = 2

…we can then define an array of pieces and make statements like the following:

DIM piece(1:%MAXPIECES, 1:%NPARAM)

piece(1, %NTYPE) = %KING

piece(1, %RANK)  = 4

piece(1, %FILE)  = 1

This sets up a 32 x 3 array for piece information.  The first element is the type of unit, the second and third give its current position on the board.  Note how much more readable this is than:

DIM piece(1:32, 1:3)

piece(1, 1) = 1

piece(1, 2) = 4

piece(1, 3) = 1

We could achieve a similar effect by using comments, but there is no way to ensure that when the program changes, the comments will be updated.  Using equates reduces the need for comments.

Besides being more readable, equates allow us to easily change a program by changing only the definition of a single equate, rather than changing every occurrence of a particular value.  For example: say you run a preschool, and you want to keep track of some data that depends on how many kids you have.  Furthermore, you have to print out reports each week.  Rather than type the number in several places, only to have to change it every week, you can assign the number to a constant.

%NUMKIDS = 28

Then, you can use the constant, %NUMKIDS, throughout your program.

' Calculate income; the enrollment fee is $85 a week;

' Parents pay whether their kids miss days or not

income% = %NUMKIDS * 85

' Calculate actual attendance

attend% = %NUMKIDS - absent%

' Calculate how much the lunches cost per kid; note the

' use of another constant for cost; it may vary too!

perkid% = %LUNCHCOST / attend%

' Calculate net profit per kid after paying for lunches (you'd

' actually have far more overhead than this, but we'll keep it simple)

net% = (income% - perkid%) / %NUMKIDS

' and so on

If your enrollment stays stable, you still have a program that is much easier to follow.  Moreover, if your enrollment changes, you only need to change the constant assignment statements to run a revised program.  Think of the time you will save - enough to take the kids on an extra field trip.

You might also want to assign the value of an equate conditionally, using the #IF metastatement.  For example:

%BIGCLASS = 1

#IF %BIGCLASS

  %NUMKIDS = 40

#ELSE

  %NUMKIDS = 20

#ENDIF

Equates make SELECT statements more readable too:

SELECT CASE piece(x, %NTYPE)

  CASE %KING

    ' process king moves

  CASE %PAWN

    ' process pawn moves

  CASE %QUEEN

    ' process queen moves

END SELECT

This code will continue to make sense when you return to it after a long absence.

Numeric equates may be assigned a specific integer-class if the literal value has a type-specifier appended.  For example:

%MAX_BYTE      = 255?

%MAXIMUM_INT   = 32767%

%MAXIMUM_DWORD = &HFFFFFFFF???

%MAXIMUM_LONG  = &H7FFFFFFF&

%MINIMUM_LONG  = &H80000000&

Numeric equates which are derived from an equation are pre-calculated by PowerBASIC during the compilation process, to ensure that unnecessary calculations are eliminated from the executable code.  If this optimization was not performed, PowerBASIC code would need to perform the same calculation every time the equate was used in the code.  Examples of numeric equates derived from expressions follows:

%WHATEVER1 = 10

%WHATEVER2 = (%WHATEVER1 * 3) + 1

%DEBUG     = -1&

%RELEASE   = NOT %DEBUG

%DEMO      = %RELEASE AND (NOT %DEBUG)

During compilation the actual numeric value of %WHATEVER2 is pre-calculated as 31, and the values of %RELEASE and %DEMO are calculated from the value of %DEBUG.  Note that operators like AND and OR work as bitwise operators, rather than logical operators, in numeric equate assignments.

Duplicate definitions of both numeric and string equates are permitted by PowerBASIC, provided the actual equate content is identical.  If the content is not identical, a compile-time Error 468 ("Duplicate Equate") will occur.

If you need a set of equates which are logically related, you can define them as a group in an enumeration.  This provides meaningful names for the enumeration, its members, and therefore the name by which it is referenced.

When an equate is created in an enumeration, its name is composed of a leading percent sign (%), the enumeration name, a period (.), and then the member name.  For example:

ENUM abc

  count = 7

END ENUM

In the above example, the equate is referenced as %abc.count, and returns the value seven (7).

Each member of an enumeration may be assigned a specific integral value (in the range of a 64-bit quad integer) by using the optional [=value] syntax.  In this case, only a constant value (or a simple constant/literal expression) may be assigned to it.  If an expression is used, all of the terms in the expression must be constants; numeric equates; bitwise operators like AND, OR, NOT; arithmetic operators +, -, *, /, \;  the relational operators >, <, >=, <=, <>, =; and the CVQ function.

If the [=value] option is omitted, each member of the enumeration is assigned an integral value in sequence beginning with the value 0.  If one or more equates are assigned an explicit value, equates which follow are assigned the next value in the sequence.  For example:

ENUM abc

  direction

  count = 8

  scope

END ENUM

In the above example, %abc.direction = 0, %abc.count = 8, and %abc.scope = 9.

 

See Also

Constants and Literals

Defining Constants

Built-in numeric equates

String Equates

Built-in string equates

ENUM/END ENUM statements