!pr1
Decimal Floating Point Arithmetic..........Bob Sander-Cederlof

Perhaps you have wondered why PRINT INT(14.9 * 10) in Applesoft prints 148.  This and many other such seeming bugs are a very common idiosyncrasy in the computer world.

Applesoft use binary floating point format for storing numbers and doing arithmetic.  The number 14.9 is very clean in decimal, but it is an awful mess in binary.  If you look at what is stored in RAM after doing X=14.9, you will find 84 6E 66 66 66.  The first byte, 84, means the remaining four should be understood as four bits of binary integer (the "14" of "14.9") and 28 bits of binary fraction (the ".9" part).  The first bit of the second byte is zero, which means the number is positive.  Applesoft stores the sign in this bit position, knowing that ALL values other than 0.0 will have a 1-bit in this position of the magnitude.

Just before doing any arithmetic on the value above, Applesoft will unpack it, separating the sign, binary exponent, and the rest.  The fancy name for the rest is the "mantissa".  Writing out the mantissa for 14.9 we see EE 66 66 66.  The first "E" means 14, and the .E666666 is APPROXIMATELY equal to .9.  It is actual less than .9 by .000000066666666...forever.  Since the number is not quite 14.9, multiplying by 10 gives not quite 149.  And taking the INT of not-quite-149 gives the CORRECT answer of 148.

CORRECT, but not what you WANTED or EXPECTED.  Right, Ethan?  That is why you will find business software written in Applesoft is full of little fudge factors.  We always need to multiply by enough 10's to make all pennies into integers, and then round up, and then truncate.

An alternative is to use DECIMAL arithmetic.  And guess what:  the 6502 has built-in decimal arithmetic.  The only trouble is that Applesoft does not know about it.

I wrote an Applesoft extension package called DPFP which gives Applesoft 21-digit precision, rather than the normal 9.  But it is still binary, so you still get those round-off and truncation problems with clearcut decimal fractions.  About two and a half years ago I wrote another Applesoft extension package called DP18.  This one is DECIMAL, and gives 18-digit precision.  Bobby Deen helped me flesh it out with full support for arithmetic expressions and all the math functions.

Well, it has been hiding on my shelf long enough!  I am going to start publishing it in AAL, a piece at a time.  In this issue you will find the routines for addition and subtraction.

First a word about the way DP18 stores numbers.  Since Applesoft uses five bytes for each floating point value, and since it is relatively easy to connect to Applesoft using multiples of five bytes, I use ten bytes for each DP18 value.  The first byte holds the sign and exponent for the value.  The remaining nine bytes hold 18 decimal digits, in BCD format.  That is, each digit takes four bits.
The first bit of the first byte is the sign bit.  Zero means plus, one means minus.  If the whole first byte is zero, the whole number is zero.  The remaining seven bits of the first byte are the decimal exponent, excess $40.  The value $40 means ten to the zero power.  $41 means 10, $42 means 100, and so on.  $3F means .1, $3E means .01, and so on.  Thus the exponent range is from $01 through $7F, meaning from 10^-63 through 10^64.

The mantissa bytes are considered to be a decimal fraction.  The number is stored so that the most significant digit is always in the first nybble of the first byte, and the exponent is adjusted accordingly.  Let's look at a few examples:

       42 14 90 00 00 00 00 00 00 00  =  14.9
       41 31 41 59 26 53 58 97 93 23  =  pi
       38 50 00 00 00 00 00 00 00 00  =  .000000005
       B8 50 00 00 00 00 00 00 00 00  =  -.000000005

Since listing the whole program at once is impossible, I have jumped right down to the lowest level so you can see how the elementary functions of addition and subtraction work.  I put the origin at $0800 for this listing, but of course the final package will run wherever you assemble it for.  Later we will get into I/O conversions, multiply and divide, math functions, print using, conversions between Applesoft and DP18 values, handling expressions with precedence and parentheses, and the linkage between DP18 and Applesoft.

The listing shown below has two main entry points, DSUB and DADD.  You can guess what they mean!  The two values to be operated on will already be unpacked into DAC and ARG by the time DSUB or DADD is called.  Note that there is one extra byte for each accumulator, so that series of calculations will carry around an extra two digits of precision to avoid rounding errors.  Unpacking a value into DAC involves storing the exponent byte in DAC.SIGN and then stripping the sign bit from DAC.EXPONENT.

DSUB and DADD both begin with the easiest cases, in which at least one of the values is zero.  DSUB complements the value in DAC by merely toggling the sign bit, and then falls into DADD.  In other words, ARG-DAC is the same as ARG+(-DAC).

DADD then determines which of the two values has the larger exponent.  If necessary, it swaps ARG and DAC:  the object is to have the value with the larger exponent in DAC (unless they are the same).  Then the value in ARG is shifted right N digits, where N is the difference in the exponents.  This what our teachers called "lining up the decimal points".

The subroutine which shifts ARG right N digits is rather smart.  First, it will just fill ARG with zeros if the shift is 20 or more.  Next, if the shift count is odd, it shifts right one digit position, or four bits.  Then it does a direct move to shift the rest of the digits by N/2 bytes, and fills in with zero bytes on the left.

Addition is divided into two cases:  either both arguments have the same sign, or they are different.  If they are both the same, a simple addition loop is used.  If the result carries into the next digit, DAC is shifted right one digit and a "1" is installed in the leftmost digit.

Otherwise, ARG is subtracted from DAC.  If both ARG and DAC had the same exponents, it is possible that the value in ARG is larger than the value in DAC.  In this case the subtracion loop will end with a "borrow" status, so the result needs to be complemented.  I complement by subtracting from zero.  Note that the three loops just described are all performed with the 6502 in decimal mode (the SED opcode at line 1490).  CLD later reverts back to binary mode.  After the mantissas are combined, the result may have one or more zero digits on the left.  Therefore we go to a NORMALIZE subroutine.

NORMALIZE shifts the mantissa left until a non-zero digit is in the leftmost digit position.  It also decrements the exponent for each digit-shift.  I tried to do the shifting involved as intelligently as possible.
