The following program acts as a calculator, with the ability to add, multiply, and divide. The unusual feature provided by the program is that it will calculate to an unlimited accuracy, restricted only by the amount of memory available to the program.
The program uses reverse Polish notation, also known as Polish
suffix notation, in which the operator follows the operand or
operands. This notation is used on some scientific calculators
because it allows any expression to be entered without the need
for brackets.
An expression in reverse Polish notation is:
1 2 + 4 5 + *
To understand how this is evaluated, imagine the expression read from left to right. When an operator, such as '+' and '*, is encountered it removes the two numbers to its left, performs the operation, and replaces them with the result. Successive stages in the evaluation of this equation are:
1 2 + 4 5 + * 3 4 5 + * 3 9 * 27
The equivalent expression in algebraic notation is (1+2)*(4+5). The great advantage of this notation is that the order of evaluation is unambiguous, and no brackets are needed to indicate how the expression is to be evaluated. Some other equations in algebraic form are shown below together with their equivalents in reverse Polish:
Algebraic: Reverse Polish: 2 + 3 + 4 2 3 + 4 + or 2 3 4 + + 3 * (4 + 5) 3 4 5 + * (3 * 4) + 5 3 4 * 5 +
The above example would be entered into the calculator program as shown below. The '?' is the prompt, and after each number or operator is entered RETURN should be typed. The program prints out the result after each operation:
?1 ?2 ?+ = 3 ?4 ?5 ?+ = 9 = 27
Sample run
In the following example using larger numbers the calculator works out 10000000001/99009901:
?10000000001 ?99009901 = 101
A number can be squared, by duplicating it on the stack with " and then multiplying:
?111111111111 ?" = 111111111111 ?* = 12345679012320987654321
Finally, we divide the result by the square of 111111:
?111111 p" = 111111 p* = 12345654321 ?/ = 1000002000001
These results are all exact, and could not of course be obtained with a conventional calculator.
Program Operation
In these programs the long numbers are represented as strings of ASCII characters. The arithmetic routines to multiply, divide, and add, take two strings and generate a result in the form of a third string. This representation was chosen so that the standard BASIC string operations could be used to manipulate long numbers and print them out; other representations would probably require special routines for these functions, but might give faster calculation.
BBC Computer Version
5 REM ... Calculator 10 DIM M%240,SS%(10),F%1023 20 S%=0:SS%(S%)=F%:Z%=ASC("0") 30 REPEAT INPUT LINESM%
Look for digit, or one of the following commands:
" - duplicate item on stack
* - multiply
+ - add
/ - divide
40 IF?N%>=ASC("0") AND ?M%<=ASC("9") $F%=$M%: PROCUP:PROCF: UNTIL0 45 IFS%>0 AND ?M%=ASC""""A%=SS%(S%): D%=SS%(S%-1):PROCUP:PROCRESULT:UNTIL0
Make sure there are at least 2 items on the stack, and then
set up:
D - top of stack (for result).
B - first operand on stack
A - second operand on stack
46 IF S%<2 PRINT"STACK EMPTY%: UNTIL 0 48 D%=SS%(S%): BS=SS%(S%-1): AS=SS%(S%-2) 50 IF?M%=ASC"*"PROCMUL: PROCDONE: UNTIL 0 55 IF?M%=ASC"+"PROCADD: PROCDONE: UNTIL 0 58 IF?M%=ASC"/" PROCDIV: PROCDONE: UNTIL 0 60 PRINT%ERROR": UNTIL 0
PROCDONE - Decrement stack, then drop through to put result on stack.
90 DEF PROCDONE S%=S%-1
PROCRESULT - Result is in D. Remove leading zeros; then copy result down stack, and print result.
92 DEF PROCRESULT 93 D%=D%-1:REPEAT D%=D%+1: UNTIL ?D%<>Z% OR D%?1=13 95 $A%=$D%: FR=A%: PROCF 100 PRINT " = "$SS%(S%-1) 110 ENDPROC
PROCF - Make room for result on top of stack.
500 DEF PROCF F%=F%+LEN($F%)+1: SS%(S%)=F%: ENDPROC
PROCMUL - Multiply: $D% = $A% * $B%
First set L% to length of result, and zero $D%. Then do long
multiplication.
1000 DEF PROCMUL 1005 L%=LEN($A%)+LEN($B%): FOR N%=0 TO L%-1: D%?N%=Z%: NEXT: D%?L%=13 1010 FOR J%=LEN($B%)-1 TO 0 STEP -1: C%=0: G%=B%?J%-Z% 1020 V%=D%+J%+1 1030 FOR L%=LEN($A%)-1 TO 0 STEP -1: H%=A%?L%-Z% 1040 Q%=G%*H%+C%+(V%?L%-Z%) 1050 V%?L%=Q% MOD 10+Z%: C%=Q%/10:NEXT 1060 V%?L%=C%+Z% 1070 NEXT:ENDPROC
PROCADD - Addition: $D% = $A% + $B%
Set result to longest operand, and add in other operand.
2000 DEF PROCADD 2005 W%=A%:V%=B%: J%=LEN($A%)-LEN($B%): IFJ%<0 W%=B%:V%=A%:J%=-J% 2010 $(D%+1)=$W%:?D%=Z%:C%=0:W%=D%+J%+1 2020 FOR L%=LEN($V%)-1 TO 0 STEP -1 2030 Q%=W%?L%+V%?L%-2*Z% 2040 W%?L%=Q% MOD 10+Z%: C%=Q%/10 2050 NEXT:W%?L%=W%?L%+C%:ENDPROC
PROCDIV - Division: $D% = $A% / $B%
Keep subtracting divisor, counting in V%, using C% as a borrow
this time, until overflows (C%=0); then add divisor back in once.
4000 DEF PROCDIV 4005 FOR J%=0 TO LEN($A%)-LEN($B%):W%=A%+J%:V%=-1 4010 REPEAT V%=V%+1:C%=1 4020 FOR L%=LEN($B%)-1 TO -J% STEP -1 4025 Q%=Z%: IF L%>=0 Q%=B%?L% 4030 Q%=W%?L%-Q%+C%+9 4032 W%?L%=Q% MOD 10 +Z%:C%=Q%/10 4035 NEXT 4040 UNTIL C%=0 4050 FOR L%=LEN($B%)-1 TO -J% STEP -1 4055 Q%=Z%:if L%>=0 Q%=b$?l% 4060 Q%=W%?L%+Q%-2*Z%+C% 4065 W%?L%=Q% MOD 10+2%:C%=Q%/10:NEXT 4070 D%?J%=V%+Z%:NEXT:D%?J%=13 4080 ENDPROC
PROCUP - Increment stack
6000 DEF PROCUP IF S%>10 PRINT "STACK FULL":ENDPROC 6010 S%=S%+1:ENDPROC
Variables:
$A%,$B% - Strings containing the two operands used by the arithmetic routines C% - Carry/borrow $D% - String into which result is put H% - Temporary variable J%,L% - Loop counters M% - Input line Q% - Intermediate result in calculations S% - Next free stack pointer SS%(0)..SS%(10) - Pointers to number strings on stack V%,W% - Pointers used by arithmetic routines Z% - Equal to ASC("0")
Atom Version
5 REM ... CALCULATOR ... 10 DIM M(64),SS(10),F(-1) 20 S=0;SSS=F; Z=CH"0" 30 DO INPUT$M
Look for digit, or one of the following commands:
" - duplicate item on stack
* - multiply
+ - add
/ - divide
40 IF?M>=CH"0"AND?M<=CH"9" $F=$M; GOSUB u;GOSUB f;GOTO x 45 IFS>0AND?M=CH""""A=SSS; D=SS(S-1);GOSUB u;GOTO w
Make sure there are at least 2 items on the stack, and then
set up:
D - top of stack (for result).
B - first operand on stack
A - second operand on stack
46 IFS<2 PRINT"STACK EMPTY"';GOTO x 48 D=SSS;B=SS(S-1);A=SS(S-2) 50 IF?M=CH"*" GOSUB m;GOTO z 55 IF?M=CH"+" GOSUB a;GOTO z 58 IF?M=CH"/" GOSUB d;GOTO z 60 PRINT"ERROR"';GOTO x 90zS=S-1
w - Result is in D. Remove leading zeros; then copy result down stack, and print result.
92wD=D-1;DO D=D+1; UNTIL?D<>Z OR D?1=13 95 $A=$D; F=A;GOSUB f 100 PRINT " = "$SS(S-1)' 110xUNTIL 0
f - Make room for result on top of stack.
500 f F=F+LENF+1; SSS=F; RETURN
m - Multiply: $D = $A * $B
First set L to length of result, and zero $D. Then do long
multiplication.
1000mL=LENA+LENB;FOR N=0 TO L-1; D?N=Z;NEXT;D?L¿13 1010 FOR J=LENB-1 TO 0 STEP -1;C=O;G=B? J-Z 1020 V=D+J+1 1030 FOR L=LENA-1 TO 0 STEP -1;H=A?L-Z 1040 Q=G*H+C+(V?L-Z) 1050 V?L=Q%10+Z;C=Q/10;NEXT 1060 V?L=C+Z 1070 NEXT; RETURN
a - Addition: $D = $A + $B
Set result to longest operand, and add in other operand.
2000aW=A;V=B; J=LENA-LENB;IFJ<0 W=B;V=A; J=-J 2010 $(D+1)=$W;?D=Z;C=0;W=D+J+1 2020 FOR L=LEN V-1 TO 0 STEP -1 2030 Q=W?L+V?L-2*Z 2040 W?L=Q%10+Z;C=Q/10 2050 NEXT; W?L=W?L+C; RETURN
d - Division: D = $A / $B
Keep subtracting divisor, counting in V, using C as a borrow this
time, until overflows (C=0); then add divisor back in once.
4000dFOR J=0 TO LEN A-LEN B; W=A+J; V=-1 4010 DO V=V+1;C=1 4020 FOR L=LEN B-1 TO -J STEP -1 4025 Q=Z; IF L>=0 Q=B?L 4030 Q=W?L-Q+C+9 4032 W?L=Q%10+Z; C=Q/10 4035 NEXT 4040 UNTIL C=0 4050 FOR L=LEN B-1 TO -J STEP -1 4055 Q=Z; IF L>=0 Q=B?L 4060 Q=W?L+Q-2*Z+C 4065 W?L=Q%10+Z;C=Q/10;NEXT 4070 D?J=V+Z;NEXT;D?J=13 4080 RETURN
u - Increment stack
6000uIF S>10 PRINT"STACK FULL"';RETURN 6010 S=S+1;RETURN
Variables:
$A,$B - Strings containing the two operands used by the arithmetic routines C - Carry/borrow $D - String into which result is put H - Temporary variable J,L - Loop counters M - Input line Q - Intermediate result in calculations S - Next free stack pointer SS(0)..SS(10) - Pointers to number strings on stack V,W - Pointers used by arithmetic routines Z - Equal to CH"0"
Further Suggestions
The calculator could be extended to handle negative numbers, represented by a string starting with a sign, and subtraction could then be implemented. As a more ambitious undertakinq, the calculator could be extended to give arbitrary-precision versions of all the integer functions of a standard pocket calculator, including X^Y, factorials, and probability functions.
A more ambitious extension would make these routines the basis of an interpreter, which would allow proqrams to be written manipulating numbers to unlimited accuracy.