!pr2
18-Digit Arithmetic, Part 6................Bob Sander-Cederlof

This month's installment will cover some of the elementary functions:  VAL, INT, ABS, SGN, and SQR.  I will also introduce a general polynomial evaluator, which will be used by most of the other math functions.

Most of the functions expect a single argument, which will be loaded into DAC by the expression evaluator just before calling the function code.  The function code will compute a value based on the argument, and leave the result in DAC.  As the expression evaluator calls with JSR, the function code returns with RTS.

One exception to the above paragraph is the VAL function.  VAL processes a string expression, and converts it into a value in DAC.  The code in lines 1350-1610 of the listing closely parallels the VAL code in the Applesoft ROMs.  Lines 1350-1370 evaluate the string expression.  Lines 1380-1460 save the current TXTPTR value (which points into your Applesoft program), and makes TXTPTR point instead at the resulting string.  Lines 1470-1520 save the byte just past the end of the string and store a 00 terminator byte in its place.  FIN will evaluate the string, placing the numeric value into DAC.  Then lines 1540-1600 restore the byte after the string and TXTPTR.

The INT function zeroes any digits after the decimal point in a number.  A number in DAC has 20 digits.  The exponent will be $00 if the value is zero, $01-40 if the value is all fractional, $41-53 if the value has from 1 to 19 digits before the decimal point, or $54-7F if the value has no fractional digits.

Lines 1650-1700 remove the $40 bias from the exponent.  If the exponent was $00-40, DP.ZERO will force DAC to zero.  Lines 1730-1740 check for the case of no fractional digits, and exit immediately.  Lines 1750-1860 zero the digits after the decimal point.  If the exponent was odd, there is one digit to be removed in the first byte to be cleared; the rest get both digits zeroed.

The simplest function is ABS, or absolute value.  All it requires is forcing the sign positive, handled at lines 1910-1930.

Almost as simple is SGN, or sign function.  SGN returns -1, 0, or +1, according as DAC was negative, zero, or greater-than- zero.  Lines 1970-1980 check DAC.EXPONENT, which will be zero if-and-only-if DAC is zero.  If the value is not zero, lines 1990-2030 force the value to be 1.0, while retaining the original sign.

SQR, the square root function, is more interesting.  Do you remember the way you learned to take square roots in high school?  Neither do I, but there is a handier way in computers anyway.
!np
Suppose I want to find square root of 25.  I could start with a wild guess, check it to see if I am close by squaring and comparing with 25, and then refining my guess until it is as accurate as I need.  Suppose my wild guess is 7 (pretty wild!).

7*7 is 49, which is bigger than 25, so my next guess should be less than 7.  Instead of just guessing wildly for the next one, why not take the average between 7 and 25/7?  That average is 5.286.  The average of 5.286 and 25/5.286 is 5.0076.  The next one is 5.0000079.  You can see that I am rapidly approaching the answer of 5.0.

The method of refining an approximation as exemplified above was derived originally be Sir Isaac Newton.  His method involves calculus, can get quite complex, and applies to all sorts of problems.  But in the case of the square root, it is as simple as averaging an approximation with the argument divided by the approximation.

It turns out that it is a very good method, because if you can get an initial approximation that has the first few digits right, the number of digits that are correct will slightly more than double each time you run through Newton's improver.

The next trick is to reduce the range of possible arguments from the full range of zero to 10^63 down to the range from .1 to 1.0.  The zero case is easy, because SQR(0) = 0, and is handled at lines 2100-2110.  Notice that lines 2120-2130 weed out negative arguments, which are not allowed.

Remember that the square root of X*10^n is equal to SQR(X)*10^(n/2).  Lines 2150-2190 save the exponent, and change it to $40.  This changes the value in DAC to the range .1 to 1.0.  I have a book which gives polynomial approximations to the square root in that range.  One with the form aX^4+bX^3+...+e gives an approximation with is accurate in the first 2.56 digits.  Three iterations by Newton yield more than 22 accurate digits.  The same book shows a cubic polynomial which gives 2.98 accurate digits if we can get the value into the range between .25 and 1.0.

Lines 2200-2280 fold the values between .1 and .25 up to the range .4 through 1.0 by multiplying the value by 4.  (This multiplication goes pretty fast, since most of the bytes are zero.)  The fact that we quadrupled the value is remembered, so that we can later halve the approximate root at lines 2350-2410.  The cubic polynomial is evaluated in lines 2290-2340, by calling POLY.N.  The result, by the time we reach line 2420, is an approximate square root of the number between .1 and 1; now we need to make it an approximate root of the original argument.

Lines 2420-2480 compute the exponent of the square root, by simply dividing the original exponent by two.  If there is a remainder, meaning the original exponent was odd, then we also need to multply the trial root by SQR(10).  This is handled in lines 2490-2550.  The halved original exponent next is added to the trial exponent, giving a good first approximation to the square root of the original argument.  Lines 2600-2740 run through three cycles of the Newton iteration, giving plenty of precision.  If we were carrying enough digits along, the 2.98 digits of precision our polynomial produced would be refined to a full 26 digits, according to my book.

Speaking of the book, it is one I bought a number of years ago when working on double precision math for a Control Data 3300 time sharing system.  As far as I know, it is still the best book in its field.  "Computer Approximations", by J. F. Hart and about seven other authors, was published in 1968 by John Wiley & Sons.  I don't know if it is still in print or not, but if you ever need to create some high precision math routines, you ought to try to find a copy.

A very common element in the evaluation of many math functions is an approximation to the function over a limited range by a polynomial, or by the quotient of two polynomials.  Therefore it is handy to have an efficient subroutine to evaluate a polynomial.  Two different entry points allow efficient evaluation of two kinds:  those whose first coefficient is 1, and the rest.  POLY.N evaluates those whose first coefficient is not one, and POLY.1 does those whose first is 1.

       POLY.N --  a*x^n + b*x^n-1 + ...
       POLY.1 --    x^n + a*x^n-1 + ...

In both cases, you enter with the address of a table of coefficients in the Y- and A-registers (hi-byte in Y, lo-byte in A), and the degree of the polynomial in the X-register.  Thus you see that in lines 2290-2340 the table P.SQR is addressed, and the degree of polynomial is 3 (cubic).  Both POLY.N and POLY.1 assume that the value of x is in TEMP2.  Where all terms have been computed and added, the result will be in DAC.

Actually, I may have misled you a little in the last sentence.  The terms of the polynomial are not separately computed and added, but rather they are accumulated in a simple serial fashion:

       poly = ((( a * x + b) * x + c) * x + d) * x + e

The coefficients and other constants shown in lines 2770-2830 are in a special format which includes an extra two digits.  You will remember that the basic operations (+-*/) are carried out to 20 digits.  Therefore these constants are carried out to 20 digits.  They are not critical in the square root computation, thanks to Sir Isaac, but the log and trig functions will need them.
