Jonathan E. Sisk's
Pick/BASIC: A Programmer's Guide
Chapter 8 - Numeric Data Conversion and Output Routing
In program example 5, two of the types of information were discussed that must be converted internally before they are stored. The third, and perhaps most common, type of data that requires internal conversion is money.
There's an important rule for you to commit to memory here:
Money is always stored internally as the number of pennies!
That's because the Pick report writer, ACCESS, needs it this way. So, in Example 6, the conversion process for handling dollar amounts is covered.
In all of the preceding examples, each PRINT statement that was used to output a literal, variable, or expression to the screen simply printed at the next available screen line. In other words, no special screen formatting took place. Example 6 illustrates formatting screens using a set of PICK/BASIC intrinsic functions. Additionally, the MC and MR conversion cedes are introduced. Topics, statements, and functions covered include the "@" function, ICONV, and OCONV (MR/MC).
Using the listing in Fig. 8-1, enter Program Example 6.
Fig. 8-1. Program Example 6.
001 * EX.006
002 * TERMINAL OUTPUT FORMATTING, MONEY CONVERSIONS
003 * mm/dd/yy: date last modified
004 * JES: author's initials
006 PROMPT "·"
008 PRINT @(-1): @(15,0): "EXAMPLE 6": @(58,0): TIMEDATE()
010 * GET FIRST NAME
013 PRINT @(5,3): "FIRST NAME": @(35,3): STR("_",25): @(35,3):
014 INPUT FIRST.NAME,25
015 UNTIL FIRST.NAME # "" DO REPEAT
016 IF FIRST.NAME = "QUIT" THEN STOP
018 * GET LAST NAME
021 PRINT @(5,5): "LAST NAME": @(35,5): STR("_",25): @(35,5):
022 INPUT LAST.NAME,25
023 UNTIL LAST.NAME # "" DO REPEAT
024 IF LAST.NAME = "QUIT" THEN STOP
026 * PUT NAMES TOGETHER AND CONVERT TO UPPER AND LOWER CASE
028 WHOLE.NAME = FIRST.NAME : " " : LAST.NAME
029 WHOLE.NAME = OCONV(WHOLE.NAME,"MCT")
031 * GET ANNUAL SALARY
034 PRINT @(5,7): "ANNUAL SALARY": @(35,7): STR("_",9): @(35,7):
035 INPUT SALARY,9
036 UNTIL SALARY = "QUIT" OR NUM(SALARY) DO REPEAT
037 IF SALARY = "QUIT" THEN STOP
039 * GET NUMBER OF PAYCHECKS
042 PRINT @(5,9) : "HOW MANY PAYCHECKS DO YOU GET EACH YEAR?"
043 PRINT @(5,10): "ENTER A NUMBER BETWEEN 1 & 52": @(-4):
044 INPUT CHECKS
045 UNTIL (CHECKS >= 1 AND CHECKS <= 52) DO REPEAT
046 IF CHECKS = "QUIT" THEN STOP
048 PRINT @(5,11): "THANKS. NO MORE QUESTIONS"
050 * START CALCULATIONS
052 INTERNAL.SALARY = ICONV(SALARY,"MR2") ; * CONVERT
053 PAYCHECK.AMOUNT = INTERNAL.SALARY / CHECKS; * CALC. AMT
055 * PRINT PSEUDO- CHECK
057 PRINT @(5,15): STR("*",50) ; * PRINT STARS
058 PRINT @(10,16): "CHECK 123" "L#20": ; * PRINT CHECK #
059 PRINT "DATE" : OCONV(DATE() ,"D") ; * TODAY'S DATE
060 PRINT @(10,17) : "PAY TO THE ORDER OF":
061 PRINT WHOLE.NAME ; * PRINT NAME
062 PRINT @(10,18) : "THE AMOUNT OF" :
063 PRINT OCONV(PAYCHECK.AMOUNT,"MR2,$*15") ; * EXTERNAL AMT
064 PRINT @(5,19): STR("*",50)
066 * DONE.
THE @ FUNCTION
There are a number of special cursor control functions in PICK/BASIC. These include a function that positions the cursor to a specific coordinate on the screen and a function that clears the screen. Each of these functions is enclosed in a set of parentheses and is preceded immediately by the @ ("at") character.
008 PRINT @(-1) :@(15,0) :"EXAMPLE 6" :@(58,0) : TIMEDATE()
The @ functions produce a string of characters. This string is most often printed immediately, but it may also be stored in a variable. Multiple @ functions may be concatenated, just like any other string. Figure 8-2 lists the most common functions used in conjunction with the PRINT statement. A more complete listing appears in Appendix B. Line 8 of the example does the following: First, it clears the screen, using @(- 1).
It then moves the cursor to column position 15 on screen line (row) 0, the top of the screen.
Function Description @(-1) Clears the screen. @(-3) Clears from the current cursor position to the end of the screen. @(-4) Clears from the current cursor position to the end of the current line. @(x,y) Positions the cursor at column (horizontal axis) "x" on row (vertical axis) "y".
Fig. 8-2. Commonly used "@" functions.
Next, it outputs "EXAMPLE 6" at the current cursor position, then moves the cursor to position 58 on screen line 0, where it next outputs the current system time and date.
Line 13 moves the cursor to position 5 on screen line 3, displays the prompt "FIRST NAME," then moves the cursor to position 35 on screen line 3, where it outputs a string of 25 "_" (underscore) characters. It then moves the cursor back to position 35 on screen line 3.
On Line 28, the two variables you had entered earlier, FIRST.NAME and LAST. NAME, are joined together (concatenated), separated by a space. The joined string is then stored in the variable WHOLE.NAME:
028 WHOLE.NAME = FIRST.NAME : " " : LAST.NAME
This is done because, on the next line of code, a conversion on the entire string is performed.
Figure 8-3 shows two examples of concatenation. Effectively, these two examples produce the same output. In Case 1, the variables are output in five separate PRINT statements. In Case 2, the variables are concatenated together and then output in one PRINT statement. There are several different schools of thought as to which of these is more efficient; I suggest that you use the one with which you feel more comfortable.
CHARACTER MASKING WITH THE OCONV FUNCTION
So far, you have seen the conversions for dates and times. A unique conversion, called the "MC" (for "Mask Character"), allows various conversions on alphabetic and/or numeric strings. For example, the "MCT" conversion converts the first alphabetic character in each word of a string to its uppercase form:
029 WHOLE.NAME = OCONV(WHOLE.NAME,"MCT")
001 PRINT NAME "L#20":
002 PRINT ADDRESS "L#25" :
003 PRINT CITY "L#20" :
004 PRINT STATE "L#10" :
005 PRINT ZIP "L#11"
001 PRINT.LINE = NAME "L#20"
002 PRINT.LINE = PRINT.LINE : ADDRESS "L#25"
003 PRINT.LINE = PRINT.LINE : CITY "L#20"
004 PRINT.LINE = PRINT.LINE : STATE "L#10"
005 PRINT.LINE = PRINT.LINE : ZIP "L#11"
006 PRINT PRINT.LINE
Fig. 8-3. Examples of concatenation.
Figure 8-4 illustrates some of the MC conversions and their effects on data. The MCU code converts all of the alphabetic characters to uppercase. The MCL code converts all of the alphabetic characters to lowercase. The MCN code retrieves all the numeric characters from the string, while the MC/N code retrieves all the nonnumeric characters. Similarly, the MCA code retrieves all the alphabetic characters (upper- or lowercase) from the string, and the MC/A code retrieves all the non-alphabetic characters. Finally, the MCT conversion capitalizes the first character of each word. Note that this conversion works fine for O'Brien, but not for McDonald.
MORE ON THE @ FUNCTION
Another twist on the @ function is shown in line 43:
043 PRINT @(5,10) :"ENTER A NUMBER BETWEEN 1 & 52" :@(-4):
Note that the last function directed to the screen before pausing to wait for user input is the intrinsic function, "@(-4)". Its purpose is to clear from the current cursor position (in this case, from two spaces to the right of the number "52") to the end of the line. This cleans up any "leftover" input when and if it is necessary to prompt the operator for this information again.
MONEY CONVERSIONS WITH ICONV
In Example 5 you examined the effect of date and time conversions. Here, the money conversion is introduced. The MR conversion is used to convert numeric amounts to their internal equivalents (remember that on dollar amounts the internal format represents the number of pennies):
052 INTERNAL.SALARY = ICONV(SALARY,"MR2") ; * CONVERT
Internal format Conversion Result 123 Main Street MCU 123 MAIN STREET 123 MAIN STREET MCL 123 main street 123 MAIN STREET MCN 123 123 MAIN STREET MCA MAINSTREET 123 MAIN STREET MCT 123 Main Street SEAN O'BRIEN MCT Sean O'Brien MEAGAN MCDONALD MCT Meagan Mcdonald
Fig. 8-4. MC conversions and their effects on data.
Money amounts are converted to internal format for several reasons. First, and probably the most important, is the fact that storing this way can save hundreds of hours in programming time, since it allows most output reports to be produced with the ACCESS language. (ACCESS does not work well when the data is not stored in internal format). Second, many powerful output conversion codes may be used when writing PICK/BASIC and/or ACCESS reports.
The MR conversion has quite a few forms. It is almost always followed by a number, which indicates the number of decimal positions expected. This number is normally "2" for dollar amounts. Another way of thinking about this number is that it represents the number of positions that the decimal point has to move to the right to convert this number to its internal equivalent. Figure 8-5 illustrates what happens when numbers are converted from external to internal format.
Figure 8-6 illustrates some of the numeric conversions that may be applied to internal numbers. The MR2 conversion places the decimal point two positions from the right end of the numeric string; when "MR2" is followed by a comma, the conversion places the decimal point two positions from the right end of the numeric string and puts commas in every third position to the left of the decimal point. Following "MR2," with a dollar sign ($) places the decimal point two positions from the right end of the numeric string and puts commas in every third position to the left of the decimal point and precedes the string with a dollar sign.
External format Conversion Result 100.22 MR2 10022 100.3 MR2 10030 100 MR2 10000 10 MR2 1000 0 MR2 0 -100.3 MR2 -10030 -100.22 MR2 -10022
Fig. 8-5. Converting money from external to internal format.
Internal OCONV format conversion Result 123456789 MR2 1234567.89 123456789 MR2, 1,234,567.89 123456789 MR2,$ $1,234,567.89
Fig. 8-6. Numeric conversions that can be applied to internal numbers.
Using Signcodes in MR Conversions
There are five special codes to activate special features on numeric amounts, four of which are for handling negative numbers. These special signcodes always appear in the same parametric position illustrated in Fig. 8- 7.
The D signcode instructs the conversion processor to output the literal "DB," for debit, after positive numbers; the other codes modify negative numbers. Conversion B in Fig. 8-7 shows the effect of not using a signcode on negative numbers. The C signcode outputs the literal "CR," for credit, after negative numbers, and the E signcode "encloses" negative amounts in the "< >" angular brackets. The M signcode "moves" the negative sign, which normally precedes negative numbers, to the right end of the number. Finally, the N signcode suppresses the leading minus sign on negative numbers. Yes, this does make negative numbers look like positive numbers. (There are applications for this, other than printing totals on profit and loss reports for failing companies.)
Fill Characters (Format Masking) in MR Conversions
The MR conversion provides a feature which fills the print field with either blanks, zeros, or asterisks; this is essentially the same as print (format) masking. The three fill operators are:
% For filling with zeros # For filling with blanks * For filling with asterisks
Internal OCONV Result format conversion provided 123456789 MR2,D$ $1,234,567,89DB -123456789 MR2,$ -$1,234,567.89 -123456789 MR2,C$ $1,234,567.89CR -123456789 MR2,E$ <$1,234,567.89> -123456789 MR2,M$ $1,234,567.89- -123456789 MR2,N$ $1,234,567,89 123456789 MR2,$*17 ****$1,234,567.89 123456789 MR2,$*10 $34,567.89
Fig. 8-7. Effects on data of signcodes and format masks used with the MR conversion.
The mask operator must be preceded by an integer number which tells it the number of characters to pad the field with. For example, the next-to-last line of Fig. 8-7 shows the use of format masks with the money conversion. The only "new" feature added here is the format mask itself (*17), which prints the number left-padded with * characters, so that the field is exactly 17 character positions wide.
Just for fun, watch what happens when the format mask is not wide enough to handle the number being printed, as in the last line of Fig. 8-7. In this example, the first two numbers are truncated because the number being printed is much larger than the mask allows for.
On line 52 of Example 6, the amount entered into the variable SALARY is converted to its internal equivalent. Once a number is in internal format, calculations may be performed or any of the output conversions just illustrated may be applied.
Line 53 calculates the amount of each paycheck by first taking the INTERNAL.SALARY variable and dividing it by the NUMBER.OF.PAYCHECKS variable. This result is then stored, still in internal format, in the variable PAYCHECK.AMOUNT.
The code on lines 57 through 64 prints a simulated paycheck on the screen, using functions and features previously covered. One added nuance is the treatment of the output on lines 60 and 61. Line 60 outputs the literal "PAY TO THE ORDER OF "and leaves the cursor positioned at the end of this message on the screen. It is important to remember that the colon at the end of the line means to suppress the carriage return normally printed at the end of a PRINT statement
Line 61 externally converts the variable, PAYCHECK.AMOUNT, with the "MR2, * 15" conversion. It then prints the external amount at the current cursor position.
Line 36 displays a string of 50 asterisks at position 5 on line 15.
REVIEW QUIZ 6
1) What instructions are required to:
A) Clear the terminal screen (two ways):
B) Print "HELLO THERE" on the 15th line at the 3rd position:
C) Clear from the cursor position to the end of the line?
2) What instruction is used to input a variable, and to limit the input to six characters?
3) What instruction is used to convert "123456.78" to its internal format?
4) What statement is used to print the external form of the number "5667788" so that it displays in the format $56,677.88?
5) What is concatenation?
6) As an exercise, modify EX.001 to validate more closely the numbers entered by the operator. Note that this program allows the operator to enter negative numbers. Prevent this from happening.