home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.installgentoo.com/Dos_Games/
/
Dos_Games.zip
/
Dos_Games
/
rpn.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
2012-11-30
|
13KB
|
381 lines
{$N+,E+} { use software floating point }
program rpn;
type CommandLineType = string[127];
var
x,y,z,t,tmp :extended; { change all "extended" to "extended" for more precision }
coms,com :string;
CommandLinePtr : ^CommandLineType;
i,j,k,n :integer;
anglemode, { 0=radians, 1=degrees }
decimals, { number of places to display }
dispmode :byte; { 0=fix, 1=sci, 2=eng, 3=fraction }
ok :boolean;
function pow(number,exponent:extended):extended;
{ implements Pascal's missing exponentiation function }
begin
if exponent=0.0 then pow:=1.0
else
if number=0.0 then pow:=0.0
else
if abs(exponent*Ln(abs(number))) > 11356.5 then
begin writeln ('Overflow in power expression'); end
else
if number>0.0 then pow:=Exp(exponent*Ln(number))
else
if (number<0.0) and (Frac(exponent)=0.0) then
if Odd(Round(exponent)) then pow:= -pow(-number, exponent)
else pow:=pow(-number, exponent)
else
begin writeln('Invalid power expression'); end;
end; (* pow *)
procedure DisplayNumber(x:extended);
var n:extended; d,p:integer; s:char; st:string;
begin
d:=dispmode;
if (d=0) and (x<>0) then
if (Abs(x)<1/(Exp(decimals*Ln(10)))) then d:=2;
case d of
0: begin { fixed mode }
Str(x:0:decimals,st);
d:=Pos('.',st);
while d>4 do { put commas in }
begin
st:=Copy(st,1,d-4) + ',' + Copy(st,d-3,200);
d:=d-3;
end;
writeln(st);
end;
1: writeln(x:decimals+9); { scientific }
2: begin { engineering }
p:=0; n:=x;
while Abs(n)>=1000 do begin n:=n/1000; p:=p+3; end;
while (Abs(n)<1) and (n<>0.0) do begin n:=n*1000; p:=p-3; end;
if p>=0 then s:='+' else begin s:='-'; p:=-p; end;
writeln(n:0:decimals-Trunc(Abs(Ln(Abs(n)+0.003123234145))/Ln(10)),' E',s,p);
end;
3: begin { fractions }
x:=x + 0.0000000000000001;
d:=1 shl Decimals; { d is largest denominator allowed }
if x>=0 then s:=' ' else s:='-'; x:=Abs(x);
{ does it not have a fractional part, within d/2? }
if (Frac(x)+1/d/2>=1) or (Frac(x)-1/d/2<0) then begin writeln(s,Round(x):0); Exit; end;
write(s,x-Frac(x):0:0,' ');
{ while the numerator is even, divide denom by two }
while ((Round(Frac(x)*d) mod 2)=0) and (d>2) do d:=d div 2;
writeln(Frac(x)*d:0:0,'/',d);
end;
end;
end;
procedure mult;
begin x:=x*y; y:=z; z:=t; end;
procedure divd;
begin x:=y/x; y:=z; z:=t; end;
procedure add;
begin x:=x+y; y:=z; z:=t; end;
procedure subt;
begin x:=y-x; y:=z; z:=t; end;
procedure perc;
begin x:=x*y/100; end;
procedure rolld;
begin tmp:=x; x:=y; y:=z; z:=t; t:=tmp; end;
procedure swap;
begin tmp:=x; x:=y; y:=tmp; end;
procedure expo;
begin x:=pow(y,x); y:=z; z:=t; end;
function csin(x:extended):extended;
begin { my own sin function to work with degrees or radians }
if anglemode=1 then x:=x*pi/180;
csin:=sin(x);
end;
function ccos(x:extended):extended;
begin
if anglemode=1 then x:=x*pi/180;
ccos:=cos(x);
end;
function carctan(x:extended):extended;
begin
if anglemode=0 then carctan:=arctan(x) else carctan:=arctan(x)*180/pi;
end;
function fact(n:extended):extended;
var t:extended;
begin
if n>1754 then begin writeln('Error - overflow in factorial'); fact:=n; Exit; end;
t:=1;
while n>1 do begin t:=t*n; n:=n-1; end;
fact:=t;
end;
function perm(n,r:extended):extended; { n! / (n-r)! }
var
t,i :extended;
begin
if r>n then begin perm:=0; Exit; end;
t:=1;
if n>r then
begin
i:=n;
while i>=(n-r+0.99) do
begin
t:=t*i;
i:=i-1;
end;
end;
perm:=t;
end;
function comb(n,r:extended ):extended; { n! / (r!) (n-r)! }
var
t,i :extended;
begin
if r>n-r then r:=n-r;
if r>n then begin Comb:=0; Exit; end;
t:=1;
if n>r then
begin
i:=n;
while i>=(n-r+0.99) do
begin
t:=t*i;
i:=i-1;
end;
end;
comb:=t/fact(r);
end;
function ArcSin (x : extended) : extended;
var t:extended;
begin
if (x<-1.0) or (x>1.0) then begin writeln('ArcSin argument out of range [-1,1]'); Exit; end;
if x=1.0 then t:=pi/2
else if x=-1.0 then t:=-pi/2
else t:=ArcTan(x/Sqrt(1.0-Sqr(x)));
if anglemode=0 then arcsin:=t else arcsin:=t*180/pi;
end; (* arcsin *)
function ArcCos (x:extended):extended;
begin
if anglemode=0 then arccos:=pi/2 - arcsin(x) else arccos:=90-arcsin(x);
end; (* arccos *)
procedure GetDecimals; { gets the single digit after fix, sci, eng, or frac }
begin
if (Length(coms)>0) and (coms[1] in ['0'..'9']) then
begin
decimals:=Ord(coms[1])-Ord('0');
Delete(coms,1,1);
end;
end;
function gamma(x:extended):extended;
var i,j :integer;
y,gam :extended;
begin { gamma function }
gamma:=x; { in case of error, use this value }
if (x<1.0) and (Frac(x)=0.0) then
begin
writeln('Error - overflow in gamma (integer < 1)');
Exit;
end;
if x>=0.0 then
begin
y:=x+2.0;
gam:=sqrt(2*pi/y)*exp(y*ln(y)+(1-1/(30*y*y))/(12*y)-y);
gamma:=gam/(x*(x+1))
end
else { x<0 }
begin
j:=0;
y:=x;
repeat
j:=j+1;
y:=y+1.0
until y>0.0;
gam:=gamma(y); { recursive call }
for i:=0 to j-1 do gam:=gam/(x+i);
gamma:=gam
end { x<0 }
end; { gamma function }
procedure help;
begin
writeln('example usage:',#13,#10,' rpn 10 15+2 3+/ => computes (10+15) / (2+3)');
writeln('use spaces to separate entry of numbers and spelled');
writeln(' operators, otherwise they are not needed.');
writeln('commands understood:');
writeln(' + - * / ^ => standard arithmetic operators');
writeln(' % => takes x% of y ( 100 15%- yields 85 )');
writeln(' c[hs] => change sign of x (minus key is for subtraction)');
writeln(' x, swap, r, roll => x-swap-y, rolldown');
writeln(' sin, cos, tan, asin, acos, atan, ln, exp, log => math functions');
writeln(' sqrt, inv, pi => square root, inverse (1/x), pi)');
writeln(' delta => computes delta percent from y to x');
writeln(' ? => use inside a calculation to diplay a result in the middle');
writeln(' stack => displays t, z, y, and x registers of stack');
writeln(' deg, rad => set degrees or radians mode for trig functions');
writeln(' comb, perm, !, gamma => combinations, permutations, factorial, gamma');
writeln(' fix[n] sci[n] eng[n] => fixed, sci, or eng, optional n is decimal places');
writeln(' frac[n] => display as fraction, with denominator at most 2^n');
writeln(' fractions: 90.1.8 => 90 1/8, 90.1.5.2 => 90 3/4 (1.5/2)');
writeln(' enter exponent: 6.02e23, 6.02e+23, or 1.3807e-23');
writeln(' q => quit');
end;
begin
dispmode:=0; decimals:=4; anglemode:=0;
x:=0; y:=0; z:=0; t:=0;
CommandLinePtr := Ptr(PrefixSeg, $80);
coms:=CommandLinePtr^; { coms is the command string }
for i:=1 to Length(coms) do coms[i]:=UpCase(coms[i]);
{ if 'Q' was on command line, don't list this }
if Pos('Q',coms)=0 then writeln('RPN calculator v1.24 Enter Q to quit H for help');
repeat
if Length(coms)>0 then
begin
ok:=false; { ok just checks that the command was understood }
case coms[1] of
'0'..'9','.':
begin
com:=''; j:=1;
while (length(coms)>0) and (coms[1] in ['0'..'9','.'])
or (Length(coms)>1) and (coms[1]='E') and (coms[2] in ['0'..'9','-','+']) do
begin
com:=com+coms[1];
if (Length(coms)>1) and (coms[1]='E') and (coms[2] in ['+','-']) then
begin
if coms[2]='-' then j:=-1 else j:=1; { flag to make exponent minus }
Delete(coms,1,1);
end;
Delete(coms,1,1);
end; { while }
t:=z; z:=y; y:=x;
{ does the number have an 'E' in it? }
if Pos('E',com)>0 then
begin
Val(Copy(com,1,Pos('E',com)-1),x,i);
Val(Copy(com,Pos('E',com)+1,100),tmp,i); tmp:=j*tmp;
x:=x * pow(10,tmp);
end
else
begin
{ does this have more than one '.'? Input a fraction }
i:=Pos('.',com); j:=Pos('.',Copy(com,i+1,100))+i;
if j=i then val(com,x,i)
else
begin { get the fraction: 90.1.8 = 90 1/8 }
while Pos('.',Copy(com,j+1,100))>0 do j:=Pos('.',Copy(com,j+1,100))+j; { j now holds pos of last dot }
Val(Copy(com,i+1,j-i-1),x,k); { get numerator of fraction }
Val(Copy(com,j+1,100),tmp,k); x:=x/tmp; { denominator }
Val(Copy(com,1,i-1),tmp,k); x:=tmp+x; { integer part }
end;
ok:=true;
end;
end; { numeric coms }
'+': begin Delete(coms,1,1); ok:=true; add; end;
'-': begin Delete(coms,1,1); ok:=true; subt; end;
'*': begin Delete(coms,1,1); ok:=true; mult; end;
'/': begin Delete(coms,1,1); ok:=true; if x<>0 then divd else writeln('Error - divide by zero'); end;
'^': begin Delete(coms,1,1); ok:=true; expo; end;
'%': begin Delete(coms,1,1); ok:=true; perc; end;
'?': begin Delete(coms,1,1); ok:=true; DisplayNumber(x); end;
'!': begin Delete(coms,1,1); ok:=true; x:=fact(x); end;
' ': begin Delete(coms,1,1); ok:=true; end;
{ is it a letter? get the whole string }
'A'..'Z':
begin
{build the whole string }
com:='';
while (Length(coms)>0) and (coms[1] in ['A'..'Z']) do
begin
com:=com+coms[1];
Delete(coms,1,1);
end;
if (com='X') or (com='SWAP') then begin ok:=true; swap; end;
if (com='R') or (com='ROLL') then begin ok:=true; rolld; end;
if com='H' then begin ok:=true; help; end;
if com='SIN' then begin ok:=true; x:=csin(x); end;
if com='COS' then begin ok:=true; x:=ccos(x); end;
if com='TAN' then begin ok:=true; if ccos(x)<>0 then x:=csin(x)/ccos(x) else writeln('Error - overflow in tan'); end;
if com='ATAN' then begin ok:=true; x:=carctan(x); end;
if com='ASIN' then begin ok:=true; x:=arcsin(x); end;
if com='ACOS' then begin ok:=true; x:=arccos(x); end;
if com='LN' then begin ok:=true; if x>0 then x:=ln(x) else writeln('Error - ln of negative number'); end;
if com='LOG' then begin ok:=true; if x>0 then x:=ln(x)/ln(10) else writeln('Error - log of negative number'); end;
if com='EXP' then begin ok:=true; if x<=11356 then x:=exp(x) else writeln('Error - overflow in exp'); end;
if com='SQRT' then begin ok:=true; if x>=0 then x:=sqrt(x) else writeln('Error - sqrt of negative number'); end;
if com='INV' then begin ok:=true; if x<>0 then x:=1/x else writeln ('Error - inverse of zero'); end;
if com='PI' then begin ok:=true; t:=z; z:=y; y:=x; x:=pi; end;
if com='COMB' then begin ok:=true; x:=comb(y,x); y:=z; z:=t; end;
if com='PERM' then begin ok:=true; x:=perm(y,x); y:=z; z:=t; end;
if com='GAMMA' then begin ok:=true; x:=Gamma(x); end;
if com='DEG' then begin ok:=true; anglemode:=1; end;
if com='RAD' then begin ok:=true; anglemode:=0; end;
if (com='C') or (com='CHS') then begin x:=-x; end;
if com='DELTA' then begin ok:=true; if y<>0 then begin x:=(x-y)/y*100; y:=z; z:=t; end
else writeln('Error - divide by zero in delta'); end;
if com='STACK' then
begin
ok:=true;
write('t: '); DisplayNumber(t);
write('z: '); DisplayNumber(z);
write('y: '); DisplayNumber(y); write('x: ');
end;
if com='FIX' then
begin
ok:=true; dispmode:=0;
GetDecimals;
end;
if com='SCI' then
begin
ok:=true; dispmode:=1;
GetDecimals;
end;
if com='ENG' then
begin
ok:=true; dispmode:=2;
GetDecimals;
end; { eng }
if com='FRAC' then
begin
ok:=true; dispmode:=3;
GetDecimals;
end; { frac }
if not ok then begin ok:=true; writeln('command not understood: ',com); end;
end; { starts with a letter }
else Delete(coms,1,1);
end; { case }
if not ok then begin ok:=true; writeln('command not understood: ',coms[1]); end;
end
else
begin
DisplayNumber(x);
readln(coms);
for i:=1 to Length(coms) do coms[i]:=UpCase(coms[i]);
if coms[1]='Q' then com:='Q';
end;
until com='Q';
DisplayNumber(x);
end.