Tl BASIC One-Liners
Michael A. Covington
The Tl BASIC DEF statement can become a powerful tool in your programmer's bag of tricks. Here's how to use it.
If you've been programming in BASIC for any time at all, you've surely come across, and used, some of the built-in functions that the language provides, such as INT, SIN, COS, TAN, ATN, and LOG. But did you know that you can use the DEF statement to create functions of your own? Defining your own functions lets you type a complicated formula only once, and it allows you to build complex functions out of simple ones in a most efficient way.
Suppose, for instance, that your LOG function gives you natural (base e) logarithms, and you want base 10 logarithms. (If you're not sure which you've got, type PRINT LOG(1O) - if the answer is 1, you're in base 10, and if it's about 2.3026, you're in base e.) You can convert base e logarithms to base 10 by dividing them by 2.302585093, so one of the options open to you is obviously to write LOG(X)/2.302585093 (or whatever) every time you need a base 10 log. But there's an easier way.
To create your own function - let's call it LOG10, though some computers may insist that you name it something like FNL - just include, early in your program, a statement like this:
10 DEF LOG10 (X) = LOG(X)/2.302585093
From then on, you'll be able to use the new function LOG10 to get base 10 logarithms. Try it out with a program something like this:
1O DEF LOG1O(X)=LOG(X)/2.3O2585O93 2O FOR 1=1 TO 10 STEP O.1 3O PRINT I,LOG1O(I) 40 NEXT I
and compare the results against a table of logarithms.
The DEF statement is different from most BASIC statements in that it can't refer to variables. (The X in it - it could be any variable name - is used only as a placeholder for the number within the parentheses; it is completely separate from any variable named X that you may use elsewhere in the program.) You can refer only to numbers or other functions. Some computers require that the name of the function be three letters and that the first two be FN - FNA, FNB, FNL, and so forth -although the TI-99, and many other microcomputers, allow you to name functions with the same type of names you use for variables.
Sample One Liners
So that's how it's done. Now let's look at some practical examples.
1. Base 10 logarithms. That's what we've just discussed. For reference, here is the statement:
DEF LOG10(X) = LOG(X)/2.302585093
(assuming your machine's LOG function gives you base e logs).
2. Base 2 logarithms. On a machine on which the LOG function gives base e logarithms, you can get base 2 logarithms by using:
DEF LOG2 (X) = LOG(X)/0.6931471806
If your machine's LOG function gives base 10 logarithms, you'll need to use DEF LOG2(X) = LOG(X)/0.3010299957 instead.
3. Degrees to radians. If X is the measure of an angle in degrees, then RAD(X) will be the same angle measured in radians, if you define the following function:
DEF RAD(X) = X/57.29577951/
4. Radians to degrees. The opposite function, converting X in radians to DEG(X) in degrees, is:
DEF DEG(X) = X*57.29577951
5. Arcsine (in radians). The following definition will give you the arcsine function (which is not usually provided in implementations of BASIC, although the arctangent is).
DEF ASN(X) = 2*ATN(X/(1 + SQR(1-X^2)))
If you look through a table of trigonometric identities, you may find an apparently equivalent, but simpler, formula that would lead to the statement DEF ASN(X) = ATN(X/SQR(1-X^2)). But note that this version won't do ASN(l) correctly (it will try to divide by zero). Hence the first version is preferable.
6. Arccosine (in radians). If you have the arcsine function, you can get the arccosine, as follows:
DEF ACS(X) = 1.570796327-ASN(X)
Remember that the DEF statement for ASN must precede the DEF statement for ACS (you can't refer to a function until you've defined it).
7. Rounding to a particular number of decimal places. Where n stands for the number of decimal places you want, use the definition:
DEF ROU(X) = INT(((l0^N)*X) + 0.5)/ l0^N)
Note that you must substitute a number for n; in most implementations, n cannot be a variable. Hence, for example, if you want rounding to three decimal places, your statement will read DEF ROU(X) = INT(((10^3)*X) + 0.5)/(l0^3). The number of decimal places can be negative, of course; if you want to round to the nearest 10, ask for -1 decimal place, and if you want to round to the nearest 1000, ask for -3 decimal places.
8. Rounding to a particular number of significant digits. Often, you'll find that the most convenient type of rounding involves coming up with a particular number of significant digits rather than a particular number of decimal places. You can accomplish this with the definition
DEF RSFl(X) = (N-l)-INT(LOGlO(X)) DEF RSF(X) = INT(((10*RSFl(X))*X) + 0.5)/(l0^ RSF1(X))
Here the definition is so complex that it is best done in two stages: first we define RSF1, which is a function used internally in RSF, and then we define RSF, which is the function we actually use. n stands for the number of significant digits you want; as before, you must substitute a number for it when typing the definition into the computer.
A word of warning: RSF (with its subsidiary calls to RSF1, which in turn calls LOG10) can take quite a bit of time to execute (about half a second of realtime on the TI-99).
9. Sexagesimal output: minutes. Our practice of expressing time in hours, minutes, and seconds, and angles in degrees, minutes, and seconds, is a remnant of an ancient Babylonian base-60 (sexagesimal) number system. Often, in a computer program dealing with time or with angles, it is desirable to express the output in terms of units, minutes, and seconds. The units are obtained by taking 1NT(X); thus the units part of 2.5 hours = INT(2.5) = 2 hours. Here is a function that gives the minutes part:
DEF MNT(X) = INT(60*(X-INT(X)))
That is, we take the non-integer part of the value, multiply it by 60, and take the INT of that.
10. Sexagesimal output: seconds. The seconds part of the value, in turn, is given by:
DEF SCD(X) = 60*(60*(X-INT(X))-MNT(X))
That is, we subtract the integer part and the minutes; what's left gets multiplied by 60 twice.
The sexagesimal output functions can be tested by means of a program such as the following:
10 DEF MNT(X)=INT(60*(X-INT(X))) 2O DEF SCD(X)=60*(6O*(X-INT(X))-MNT(X) ) 3O FOR H=O TO 2 STEP O.O1 4O PRINT 5O PRINT H,"HOURS" 6O PRINT INT(H),MNT(H),SCD(H) 7O NEXT H
From this we learn, for example, that 0.01 of an hour is 36 seconds, and that 0.5 of an hour is 30 minutes. (If your computer uses binary, rather than BCD or Radix-100, internal representations of numbers, you may get odd errors due to rounding or lack of it. The solution would be to round the number of hours to some reasonably small number of decimal places before invoking the conversions, and perhaps to insert some rounding in the definitions of MNT and SCD themselves.)
Incidentally, for sexagesimal input, you don't need any special functions, only a bit of multiplication. For instance, the statements
1O PRINT "TYPE HOURS, MINUTES, SECONDS" 20 INPUT H,M,S 3O H=H+M/6O+S/36OO
will give you (as H) the number of hours expressed as a decimal.
11. Modulo 12 arithmetic. In dealing with hours, you'll often want to reduce numbers to modulo 12. For instance, if it's 11 a.m., then you can calculate the time four hours later by adding 11+4 (which gives you 15) and then taking the result modulo 12. The function definition is:
DEF MOD12(X) = 12*(X/12-INT(X/12))
(unless, of course, your computer has a built-in MOD function, which is even simpler to use). This particular function is likely to be bothered by rounding and truncation errors. On the TI-99, I get accurate results for numbers under 1000 or so, but larger numbers give slightly erroneous answers; a binary machine might be plagued by worse problems.
12. Modulo 60 arithmetic. The same function, giving modulo 60 answers (for dealing with minutes and seconds), is:
DEF MOD60(X) =60*(X/60 - INT(X/60))
(as if you couldn't have guessed). The following program starts with a time expressed as H hours M minutes, and adds Ml minutes:
1O DEF MOD12(X)=12*(X/12-INT(X/12)) 2O DEF MOD6O(X)=6O*(X/60 INT)X/6O)) 30 INPUT H, M 4O INPUT Ml 5O M=MOD6O(M+M1) 60 H=H+INT(M1/6O) 70 PRINT H, M
Line 50 adds the right number to the minutes part, and line 60 adds to the hours part if necessary.