Basically Useful BASIC
Financial Fuzzies
Jim Butterfield
Small computers are ideal for many small business applications. But a puzzling problem crops up which could cause pennies to occasionally disappear.
It's tied into the fact that almost all computers do their arithmetic in binary. This seems innocent enough until you realize that some fractions which are exact in decimal notation turn into an endless repeating fraction in binary. For example, .1 decimal translates into binary as .000110011…
You can see the nature of the problem instantly by typing on the PET: PRINT 8.13.. the number which is actually printed is slightly different from 8.13. Or try PRINT 2.23-2.18 and see if the answer is the .05 you expect.
Don't feel too smug if you use a different make of computer and the above examples turn out all right. All binary number machines have this problem; it's there, waiting for you.
Almost all decimal fractions change to repeating binary numbers; and since you don't have infinite memory space, the number must be chopped off somewhere. Many Basics are very clever, and have rounding routines that trim the number upward or downward as seems best. Numbers are usually trimmed slightly before printing, which makes the error disappear. But the problem is still there. And the more numbers you add and subtract, the greater your error will be.
A Detailed Look
Here's a small Basic program which will allow you to look at numbers stored in your system.
100 INPUT "AMOUNT";A 110 B = INT(A) : C = A-B : ?A; "="; B;","; 120 C = C*10 : D = INT(C) : C = C-D 130 PRINT D; : IF C>0 GOTO 120 140 PRINT 150 GOTO 100
Line 100 gets the input number. You may change this to calculate values for printing, if you wish.
Line 110 takes the integer part of the value and prints it, followed by a decimal point. Variable C becomes equal to the fractional part of the value.
Line 120 multiplies C by 10; the integer value is the next digit to be printed. For example, a value of .125 would become 1.25; we'd print the 1 and keep computing using the .25 as the next C value.
You may be surprised to find that the only financial fractions that work out exactly in binary are .00, .25, .50 and .75!
By playing around a little, you'll quickly spot the fact that integers never give any trouble. It's only the decimal fractions that misbehave.
How to fix it.
The quickest way around this problem is to round the numbers when you're printing them. If you have a PRINT USING statement on your computer, or an equivalent subroutine, it should round the numbers as it formats them.
A quick rounding formula is: X = INT(X* 100+.5)/100 which will give a value to the nearest cent.
Rounding isn't the best way, however. The error is still there - the value will seldom be exact. As you add and subtract more and more numbers, the error grows. It will be particularly noticeable for large numbers (amounts over $100,000, say), and eventually a penny may get away from you even using rounding.
The best method is to change all financial numbers to pennies, and work exclusively in integer pennies. $3.21 becomes 321 pennies, for example.
Convert the numbers at the time of input. For example:
340 INPUT "AMOUNT";A : A = INT (A*100 + .5)
During your computation, remember to round percentage calculations. Add .5 to round; don't add to drop the fractional penny:
470 T = INT (A*7/100 + .5) : REM 7% TAX, ROUNDED 480 S = INT(A/S) : REM ONE THIRD, DROP FRACTION
Note that T and S remain as exact pennies.
Finally, convert back to pennies just before printing:
760 PRINT A/100 : REM OUTPUT AS DOLLARS-AND-CENTS
Editor's Note:
B. J. Deemer's interesting article presents some general hints for keying in a long program. Basically Useful BASIC would be interested in other such efforts.