123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786 |
- unit ScEvaluate;
- interface
- function Evaluate(formula: string; var status,location: integer) : double; overload;
- function Evaluate(formula: string):double; overload;
- implementation
- function Evaluate(formula: string):double;
- var
- iStatus: Integer;
- iLocation: Integer;
- begin
- Result := Evaluate(formula, iStatus, iLocation);
- end;
- {================================== EVALUATE ================================}
- { ------------------------------- Declarations ----------------------------- }
- const MaxReal = 1.0E+37; { Maximum real value that we will allow }
- MaxFact = 33; { 33! = E+37 }
- MaxExpo = 85; { exp(85) = E+37 }
- var Region, { Check if result is defined. eg fact(-1.2) }
- Divzero, { Check if a division by zero occured. eg 1/0 }
- Overflow, { Check if result becomes too large. eg 100! }
- Complex : boolean; { Check if result is complex. eg sqrt(-1) }
- { -------------------------------------------------------------------------- }
-
- {============================================================================}
- {================================== EVALUATE ================================}
- { From a VERY VERY OLD Pascal 3.0 Function !
- {============================================================================}
- { Initialize boolean flags. }
- procedure Init_Booleans;
- begin
- region:=true; { Result is initially inside region }
- complex:=false; { Result is not complex }
- divzero:=false; { There is no division by zero }
- overflow:=false; { and no overflow. }
- end; { Init_Booleans }
- { Check to see if doing an operation on a and b will cause an overflow and
- set the OVERFLOW boolean accordingly }
- procedure CheckOverflow(a,b: real; operation: char);
- begin
- case operation of
- '*': if abs(a) > 1.0 then overflow:=abs(b) > MaxReal/abs(a);
- '/': if b <> 0.0 then
- begin
- b:=1/b;
- if abs(a) > 1.0 then overflow:=abs(b) > MaxReal/abs(a)
- end
- else overflow:=true;
- '+': if b > 0.0 then overflow:=a > (MaxReal - b)
- else overflow:=a < (-MaxReal - b);
- '-': if b < 0.0 then overflow:=a > (MaxReal + b)
- else overflow:=a < (-MaxReal + b);
- else overflow:=true; { Default for bad operation }
- end; { case }
- end; { CheckOverflow }
- { ----------------------------------------------------------------------------
- The following functions -- asin,acos,tan,cot,sec,csc,sinh,cosh,tanh,sech,
- csch,coth,fact -- will default to 0.0 if a division by zero occurs, the
- result is complex or undefined, or if an overflow occurs.
- If you are using these functions independently of the Evaluate procedure
- then the procedure Init_Booleans should be called first to reinitialize
- error checking.
- ---------------------------------------------------------------------------- }
- { --- Arc Sine --- }
- function Asin(r: real): real;
- begin
- if abs(r) < 1.0 then asin:=arctan(r/sqrt(1-r*r))
- else
- if abs(r) = 1.0 then asin:=(r/abs(r))*pi/2
- else
- begin
- asin:=0.0;
- complex:=true
- end { else }
- end; { asin }
- { --- Arc Cosine --- }
- function Acos(r: real): real;
- begin
- if r = 0.0 then acos:=pi/2
- else
- begin
- if abs(r) < 1.0 then
- begin
- if r < 0.0 then acos:=arctan(sqrt(1-r*r)/r)+pi
- else acos:=arctan(sqrt(1-r*r)/r)
- end
- else
- if abs(r) = 1.0 then
- if r = 1.0 then acos:=0.0
- else acos:=pi
- else
- begin
- acos:=0.0;
- complex:=true
- end
- end
- end; { acos }
- { --- Tangent --- }
- function Tan(r: real): real;
- begin
- if cos(r) = 0.0 then
- begin
- tan:=0.0;
- divzero:=true
- end
- else tan:=sin(r)/cos(r)
- end; { tan }
- { --- CoTangent --- }
- function Cot(r: real): real;
- begin
- if sin(r) = 0.0 then
- begin
- cot:=0.0;
- divzero:=true
- end
- else cot:=cos(r)/sin(r)
- end; { cot }
- { --- Secant --- }
- function Sec(r: real): real;
- begin
- if cos(r) = 0.0 then
- begin
- sec:=0.0;
- divzero:=true
- end
- else sec:=1/cos(r)
- end; { Sec }
- { --- CoSecant --- }
- function Csc(r: real): real;
- begin
- if sin(r) = 0.0 then
- begin
- csc:=0.0;
- divzero:=true
- end
- else csc:=1/sin(r)
- end; { Csc }
- { --- Sinh --- }
- function Sinh(r: real): real;
- begin
- if abs(r) <= MaxExpo then sinh:=(exp(r)-exp(-r))/2
- else
- begin
- overflow:=true;
- sinh:=0.0
- end
- end; { Sinh }
- { --- CoSinh --- }
- function Cosh(r: real): real;
- begin
- if abs(r) <= MaxExpo then cosh:=(exp(r)+exp(-r))/2
- else
- begin
- overflow:=true;
- cosh:=0.0
- end
- end; { Cosh }
- { --- Tanh --- }
- function Tanh(r: real): real;
- begin
- if cosh(r) = 0.0 then
- begin
- tanh:=0.0;
- divzero:=true
- end
- else
- begin
- CheckOverflow(sinh(r),cosh(r),'/');
- if not overflow then tanh:=sinh(r)/cosh(r)
- else tanh:=0.0
- end
- end; { Tanh }
- { --- Sech --- }
- function Sech(r: real): real;
- begin
- if cosh(r) = 0.0 then
- begin
- sech:=0.0;
- divzero:=true
- end
- else
- begin
- CheckOverflow(1,cosh(r),'/');
- if not overflow then sech:=1/cosh(r)
- else sech:=0.0
- end
- end; { Sech }
- { --- CoSech --- }
- function Csch(r: real): real;
- begin
- if sinh(r) = 0.0 then
- begin
- csch:=0.0;
- divzero:=true
- end
- else
- begin
- CheckOverflow(1,sinh(r),'/');
- if not overflow then csch:=1/sinh(r)
- else csch:=0.0
- end
- end; { Csch }
- { --- CoTanh --- }
- function Coth(r: real): real;
- begin
- if sinh(r) = 0.0 then
- begin
- coth:=0.0;
- divzero:=true
- end
- else
- begin
- CheckOverflow(cosh(r),sinh(r),'/');
- if not overflow then coth:=cosh(r)/sinh(r)
- else coth:=0.0
- end
- end; { Coth }
- { --- Factorial --- }
- function Fact(r:real): real;
- var i: integer;
- resulta: real;
- begin
- if (r < 0.0) or (trunc(r) <> r) then
- begin
- resulta:=0.0;
- region:=false
- end
- else
- begin
- resulta:=1.0;
- if trunc(r) < MaxFact then
- for i:=1 to trunc(r) do
- resulta:=resulta*i
- else
- begin
- overflow:=true;
- resulta:=0.0
- end
- end;
- fact:=resulta
- end; { Fact }
- { ----------------------------------------------------------------------------
- The function Evaluate is passed a mathematical expression in the form of a
- string (formula) to be evaluated and returns the following:
- If no errors occur during evaluation then:
- Result = evaluated expression
- Status = 0
- Location = 0
- If an error occurs then:
- Result = 0.0
- Status = error type
- Location = location of error in formula
- Error types:
- 0: No error occured
- 1: Illegal character
- 2: Incorrect syntax
- 3: Illegal or missing parenthese
- 4: Incorrect real format
- 5: Illegal function
- 6: Result is undefined
- 7: Result is too large
- 8: Result is complex
- 9: Division by zero
- ---------------------------------------------------------------------------- }
- {const
- ErrStr: array[1..9] of string =
- ('无效字符',
- '语法错误',
- '括号不配对',
- '实数的格式部正确',
- );}
- function Evaluate(formula: string; var status,location: integer):double;
- { ---- Declaration ---- }
- const numbers: set of char = ['0'..'9']; { Digits }
- RightPar: set of char = [')',']','}']; { Right parentheses }
- LeftPar: set of char = ['(','[','{']; { Left parentheses }
- alpha: set of char = ['A'..'Z']; { Alpha characters }
- operators: set of char = ['+','-','*','/','^']; { Operators }
- eofline = ^M;
- var ch: char; { Current character }
- resulto: real; { Final value }
- { ---- Internal routines ---- }
- { Check to see if an error has occured }
- function Ok: boolean;
- begin
- ok:= region and (not divzero) and (not complex)
- and (not overflow) and (status = 0);
- end; { Ok }
- { Get the next character in the string and increment the location pointer. }
- procedure NextCh;
- begin
- repeat
- location:=location+1; { Increment pointer }
- if location <= length(formula) then ch:=formula[location]
- else ch:=eofline;
- if not (ch in alpha + numbers + LeftPar + RightPar + operators
- + ['%', '.',' ','!',eofline]) then
- status:=1; { Illegal char. }
- until ch <> ' '; { Skip blanks }
- end { NextCh };
- { ---- Nested functions ---- }
- function Expression: real;
- label quit;
- var e,e_hold: real;
- opr: char;
- Leading_Sign,Nested_Function: boolean;
- function SimpleExpression: real;
- label quit;
- var s,s_hold: real;
- opr: char;
- function Term: real;
- label quit;
- var t,t_hold: real;
- function SignedFactor: Real;
- function Factor: Real;
- label quit;
- type StandardFunction =
- (fpi,fabs,fsqrt,fsqr,fln,flog,fexp,ffact,
- fsinh,fcosh,ftanh,fsech,fcsch,fcoth,
- fsin,fcos,ftan,fsec,fcsc,fcot,fasin,facos,fatan);
- StandardFunctionList = array[StandardFunction] of string[4];
- const StandardFunctionNames: StandardFunctionList =
- ('PI','ABS','SQRT','SQR','LNG','LOG','EXP','FACT',
- 'SINH','COSH','TANH','SECH','CSCH','COTH',
- 'SIN','COS','TAN','SEC','CSC','COT','ASIN','ACOS','ATAN');
- var Found: Boolean;
- l: integer;
- F: Real;
- str: string;
- Sf: StandardFunction;
- start,position: integer;
- begin { Function Factor }
- { Exit if error }
- if not ok then begin f:=0.0; goto quit end;
- { Get a real or integer expression }
- if ch in numbers+['.'] then
- begin
- start:=location;
- if ch in numbers then repeat NextCh until not (ch in numbers);
- if ch = '.' then repeat NextCh until not (ch in numbers);
- { Get the E format of a real expression }
- if ch = 'E' then
- begin
- NextCh;
- if ch = 'X' then location:=location - 1 { Skip EXP(...) }
- else
- if not (ch in numbers + ['+','-']) then status:=4
- else repeat NextCh until not (ch in numbers);
- end;
- { Check the real format }
- str:=copy(formula,start,location-start);
- { Remove all spaces in str otherwise val will bomb! }
- while pos(' ',str) <> 0 do delete(str,pos(' ',str),1);
- val(str,f,position);
- if position <> 0 then
- begin
- location:=start+position;
- status:=4 { Incorrect Real format }
- end;
- if Ok and (Ch = '%') then
- begin
- f := f / 100;
- NextCh;
- end;
- end { end if ch in number }
- { The character is not a digit }
- else
- begin
- { Check for for the beginning of a "sub" expression }
- if ch in LeftPar then
- begin
- NextCh;
- F:=Expression; { RECURSION !!! }
- if ok and (ch in RightPar) then { Check for implicit * }
- begin
- NextCh;
- if not (ch in operators+LeftPar+RightPar+['!',eofline]) then
- begin
- ch:='*';
- location:=location-1
- end;
- end
- else status:=3 { Illegal parenthese }
- end { if ch in LeftPar }
- { It should be a function }
- else
- begin
- found:=false;
- { Search for the function among our list }
- for sf:=fpi to fatan do
- if not found then
- begin
- l:=length(StandardFunctionNames[sf]);
- if copy(formula,location,l)=StandardFunctionNames[sf] then
- begin
- location:=location+l-1;
- NextCh;
- if sf <> fpi then
- begin
- Nested_Function:=true;
- F:=Factor
- end;
- { Assign values according to the function }
- case sf of
- fpi: f:=pi; { pi is predefined }
- fsqr: if f < 1.0e+19 then f:=sqr(f)
- else
- begin
- f:=0.0;
- overflow:=true
- end;
- fabs: f:=abs(f);
- fsqrt: if f < 0.0 then
- begin
- complex:=true;
- f:=0.0
- end
- else f:=sqrt(f);
- fsin: f:=sin(f);
- fcos: f:=cos(f);
- ftan: f:=tan(f);
- fasin: f:=asin(f);
- facos: f:=acos(f);
- fatan: f:=arctan(f);
- fsec: f:=sec(f);
- fcsc: f:=csc(f);
- fcot: f:=cot(f);
- fsinh: f:=sinh(f);
- fcosh: f:=cosh(f);
- ftanh: f:=tanh(f);
- fsech: f:=sech(f);
- fcsch: f:=csch(f);
- fcoth: f:=coth(f);
- fexp: if abs(f) < MaxExpo then f:=exp(f)
- else
- if f < 0 then f:=0.0
- else
- begin
- overflow:=true;
- f:=0.0
- end;
- ffact: f:=fact(f);
- fln : if f < 0.0 then
- begin
- complex:=true;
- f:=0.0
- end
- else
- if f = 0.0 then
- begin
- overflow:=true;
- f:=0.0
- end
- else f:=ln(f);
- flog: if f < 0.0 then
- begin
- complex:=true;
- f:=0.0
- end
- else
- if f = 0.0 then
- begin
- overflow:=true;
- f:=0.0
- end
- else f:=ln(f)/ln(10);
- end; { Case }
- found:=true;
- Nested_Function:=false;
- { Check for a trailing factorial symbol }
- if ch = '!' then
- begin
- f:=fact(f);
- NextCh
- end
- end { If copy = function }
- end; { If not found }
- { Check for more errors }
- if (not found) and ok and not (ch in alpha) then
- status:=2; { Illegal Syntax }
- if (not found) and ok and (ch in alpha) then
- status:=5; { Illegal function }
- end { Else not ch in LeftPar .. ie. it should be a function }
- end; { else the character is not a digit }
- { Check for a trailing factorial symbol }
- if ok and (not Nested_Function) and (ch = '!') then
- begin
- f:=fact(f);
- NextCh
- end;
- { Assign final value }
- quit: Factor:=F
- end; { Factor inside SignedFactor }
- begin { SignedFactor }
- if ch = '-' then
- begin
- NextCh;
- SignedFactor:= -Factor
- end
- else SignedFactor:=Factor;
- end { SignedFactor inside Term };
- begin { Term }
- if not ok then begin t:=0.0; goto quit end; { Exit }
- t:=SignedFactor;
- while ch = '^' do
- begin
- if not ok then begin t:=0.0; goto quit end; { Exit }
- NextCh;
- t_hold:=SignedFactor;
- { Check for illegal power }
- if ((t < 0.0) and ((t_hold-trunc(t_hold)) <> 0.0)) or (t = 0.0) then
- begin
- t:=0.0;
- complex:=true
- end
- { Power is legal }
- else
- begin
- if t < 0.0 then
- begin
- CheckOverflow(ln(-t),t_hold,'*');
- if not Ok then begin t:=0.0; goto quit end; { Exit }
- if ln(-t)*t_hold <= MaxExpo then
- case trunc(abs(t_hold)) mod 2 = 0 of
- true: t:=exp(ln(-t)*t_hold);
- false: t:=-exp(ln(-t)*t_hold)
- end
- else
- begin
- t:=0.0;
- overflow:=true
- end
- end { if t < 0.0 }
- else { t >= 0.0 }
- begin
- CheckOverflow(ln(t),t_hold,'*');
- if not Ok then begin t:=0.0; goto quit end; { Exit }
- if ln(t)*t_hold <= MaxExpo then t:=exp(ln(t)*t_hold)
- else
- begin
- t:=0.0;
- overflow:=true
- end
- end { else t >= 0.0 }
- end { else not illegal power }
- end; { while }
- quit: Term:=t;
- end; { Term inside SimpleExpression }
- begin { SimpleExpression }
- if not ok then begin s:=0.0; goto quit end; { Exit }
- s:=term;
- { Check for implicit multiplication and insert missing "*" }
- if ok and (ch in LeftPar + alpha + numbers + ['.']) then
- begin
- ch:='*';
- location:=location-1
- end;
- while ch in ['*','/'] do
- begin
- if not ok then begin s:=0.0; goto quit end; { Exit }
- opr:=ch;
- NextCh;
- { Check for implicit multiplication and insert missing "*" }
- if opr in LeftPar + alpha + numbers + ['.'] then
- begin
- opr:='*';
- ch:='(';
- location:=location-1
- end;
- s_hold:=term;
- case opr of
- '*': begin
- CheckOverflow(s,s_hold,'*');
- if not overflow then s:=s*s_hold
- else s:=0.0
- end;
- '/': begin
- divzero:=s_hold = 0.0;
- if not divzero then
- begin
- CheckOverflow(s,s_hold,'/');
- if not overflow then s:=s/s_hold
- else s:=0.0
- end
- else s:=0.0
- end
- end; { Case }
- { Check for implicit multiplication and insert missing "*" }
- if ok and (ch in LeftPar + alpha + numbers + ['.']) then
- begin
- ch:='*';
- location:=location-1
- end
- end; { while }
- { Assign final value }
- quit: SimpleExpression:=s;
- end; { SimpleExpression inside Expression }
- begin { Expression }
- if not ok then begin e:=0.0; goto quit end; { Exit }
- Nested_Function:=false;
- Leading_Sign:= ch = '-'; { The default is + }
- if ch in ['+','-'] then Nextch; { Skip leading sign }
- case Leading_Sign of { Set for leading sign }
- true: e:= -SimpleExpression;
- false: e:= SimpleExpression
- end;
- while ch in ['+','-'] do
- begin
- if not ok then begin e:=0.0; goto quit end; { Exit }
- opr:=ch;
- NextCh;
- e_hold:=SimpleExpression;
- case opr of
- '+': begin
- CheckOverflow(e,e_hold,'+');
- if not overflow then e:=e+e_hold
- else e:=0.0;
- end;
- '-': begin
- CheckOverflow(e,e_hold,'-');
- if not overflow then e:=e-e_hold
- else e:=0.0;
- end;
- end; { case }
- end; { while }
- quit: Expression:=e;
- end; { Expression inside Evaluate }
- var i:integer;
- begin { Evaluate }
- { Initialize }
- for i:=1 to length(formula) do
- formula[i]:=upcase(formula[i]);
- Init_Booleans;
- status:=0;
- location:=0;
- NextCh;
- { Get result }
- resulto:=Expression;
- { Check for final errors }
- if ok then if ch <> eofline then status:=2; { Incorrect Syntax }
- if not region then status:=6;
- if overflow then status:=7;
- if complex then status:=8;
- if divzero then status:=9;
- if status in [4,6..9] then location:=location-1;
- if status = 0 then location:=0
- else resulto:=0.0;
- Evaluate:=resulto;
- end { Evaluate };
- end.
-
|