![]() |
Delphi-Version: 2005
Parser Kommarechnung
Hallo, ich habe folgenden Parser benutzt, doch leider beherrscht dieser keine Kommazahlen...
WIe muss ich da was ändern, damit er dies auch beherrscht???
Delphi-Quellcode:
Viele Grüße!
function TermToFloat(Const Term:string):real;
function _Eval(term: string; var i: Integer): Integer; const isNone = 0; isNum = 1; isOp = 2; var numStack: array[0..10] of Integer; number, code, sign, sp, j: Integer; Ops: string; c: Char; function OpOrder(c: Char): Integer; begin if c = '$' then Result := -1 else Result := (Pos(c, '+-*/') + 1) div 2; end; begin fillchar(numstack, sizeof(numstack), 0); sign := 1; Ops := '$'; SP := 0; number := 0; Result := 0; code := isNone; while i < length(term) do begin inc(i); c := term[i]; case c of '0'..'9': begin number := 10 * number + ord(c) - 48; code := isNum; end; '(': begin number := _Eval(term, i); code := isNum; end; ' ': continue; '+', '-', '*', '/', ')': begin if code = isNum then begin numStack[SP] := sign * number; sign := 1; inc(SP); number := 0; end else if code = isOp then begin if c = '-' then sign := -sign; Continue end; code := isop; if Length(Ops) > 1 then begin while opOrder(c) <= OpOrder(Ops[Length(Ops)]) do begin dec(sp); case Ops[Length(Ops)] of '*': numstack[sp - 1] := numstack[sp - 1] * numstack[sp]; '/': numstack[sp - 1] := numstack[sp - 1] div numstack[sp]; '+': numstack[sp - 1] := numstack[sp - 1] + numstack[sp]; '-': numstack[sp - 1] := numstack[sp - 1] - numstack[sp]; end; numStack[sp] := 0; setLength(Ops, length(Ops) - 1); end; end; Ops := Ops + c; if c = ')' then begin number := NumStack[0]; break; end; end; end; end; result := Number; end; var i: Integer; begin i := 0; Result := _Eval('(' + term + ')', i); end; |
AW: Parser Kommarechnung
Delphi-Quellcode:
dann noch decimal deklarieren und initialisieren sowie die Zahlentypen von INTEGER nach DOUBLE ändern.
....
'0'..'9': begin if decimal>0 then begin number := number + Iord(c) - 48)/decimal; decimal := decimal*10; else number := 10 * number + ord(c) - 48; code := isNum; end; '.': if decimal>0 then raise Exception.Create('invalid decimal') else decimal := 10; '(': begin decimal := 0; number := _Eval(term, i); code := isNum; end; ' ': begin decimal := 0; continue; end; '+', '-', '*', '/', ')': begin decimal := 0; .... Ungetestet, könnte aber klappen. |
AW: Parser Kommarechnung
Danke erstmal. Nach Korrektur einiger Tippfehler komme ich an einer Stelle nicht weiter. Hier nochmal alles (neu)
Delphi-Quellcode:
function TermToFloat(Const Term:string):real;
function _Eval(term: string; var i: Integer): Integer; const isNone = 0; isNum = 1; isOp = 2; var numStack: array[0..10] of Double; number, code, sign, sp, j, decimal: Double; Ops: string; c: Char; function OpOrder(c: Char): Integer; begin if c = '$' then Result := -1 else Result := (Pos(c, '+-*/') + 1) div 2; end; begin fillchar(numstack, sizeof(numstack), 0); sign := 1; Ops := '$'; SP := 0; number := 0; Result := 0; code := isNone; while i < length(term) do begin inc(i); c := term[i]; case c of '0'..'9': begin if decimal>0 then begin number := (number + ord(c) - 48)/decimal; decimal := decimal*10; end else number := 10 * number + ord(c) - 48; code := isNum; end; '.': if decimal>0 then raise Exception.Create('invalid decimal') else decimal := 10; '(': begin decimal := 0; number := _Eval(term, i); code := isNum; end; ' ': begin decimal := 0; continue; end; '+', '-', '*', '/', ')': begin decimal := 0; if code = isNum then begin numStack[SP] := sign * number; // Hier kommt der Fehler (s.u.) sign := 1; inc(SP); number := 0; end else if code = isOp then begin if c = '-' then sign := -sign; Continue end; code := isop; if Length(Ops) > 1 then begin while opOrder(c) <= OpOrder(Ops[Length(Ops)]) do begin dec(sp); case Ops[Length(Ops)] of '*': numstack[sp - 1] := numstack[sp - 1] * numstack[sp]; '/': numstack[sp - 1] := numstack[sp - 1] div numstack[sp]; '+': numstack[sp - 1] := numstack[sp - 1] + numstack[sp]; '-': numstack[sp - 1] := numstack[sp - 1] - numstack[sp]; end; numStack[sp] := 0; setLength(Ops, length(Ops) - 1); end; end; Ops := Ops + c; if c = ')' then begin number := NumStack[0]; break; end; end; end; end; result := Number; end; var i: Integer; begin i := 0; Result := _Eval('(' + term + ')', i); end; An der im Quelltext markierten Stelle kommt der Fehler "inkompatible Typen: Integer und Double" Ist doch alles Double?! Viele Grüße! |
AW: Parser Kommarechnung
Delphi-Quellcode:
...
sp : double //muss integer sein .. |
AW: Parser Kommarechnung
Oh, und wie soll ich decimal initialisieren? Also als was?
Viele Grüße! |
AW: Parser Kommarechnung
Delphi-Quellcode:
function _Eval(term: string; var i: Integer): Integer;
const isNone = 0; isNum = 1; isOp = 2; var numStack: array[0..10] of Double; number, code, sign, j, decimal: Double; Ops: string; sp : integer; c: Char; |
AW: Parser Kommarechnung
So meinte ich das nicht. Ich meine den Startwert...
Ich krieg immer eine Fehlermeldung, einen Gleitkommaüberlauf... decimal ist dabei =2.1219942e-314 number ist 0 und c=5 Fehlerzeile ist number := (number + ord(c) - 48)/decimal; |
AW: Parser Kommarechnung
Versuch mal, alzaimars Code nachzuvollziehen. Die Potenzen steigen in unserem Dezimalsystem von rechts nach links jeweils um eins an:
Code:
D.h., man muss die Ziffer an der jeweilgen Position immer mit der jeiligen Zehner-Potenz, der Wertigkeit, multiplizieren, um den Wert zu erhalten. Deshalb funktioniert es bei von links nach rechts eingelesenen Ganzzahlen, einfach immer den alten Wert mit 10 zu multiplizieren und die aktuelle Ziffer zu addieren. So macht es auch der originale Algorithmus.
1. Stelle vor dem Komma: 10^0 = __1
2. Stelle vor dem Komma: 10^1 = _10 3. Stelle vor dem Komma: 10^2 = 100 (_ = Leerzeichen, aus irgendeinem Grund bleibt hier ja der Whitespace leider nicht mehr erhalten...) usw... Hinter dem Komma ist es eben genau umgekehrt:
Code:
Hier braucht man allerdings eine zusätzliche Variable, die die Potenz speichert: Dafür ist bei Alzaimars Code die Variavble decimal zuständig, die für jede Ziffer nach dem Komma mit 10 multipliziert wird. Die neue Ziffer wird dann durch diesen Wert geteilt und zum vorigen Wert hinzuaddiert. Der Initialwert ist logischerweise 10, weil die erste Stelle nach dem Komma die Wertigkeit 1/10 hat (10^-1).
1. Stelle nach dem Komma: 10^-1 = 0.1
2. Stelle nach dem Komma: 10^-2 = 0.01 3. Stelle nach dem Komma: 10^-3 = 0.001 usw... Dieser Wert wird initialisiert, sobald das erste mal ein Komma auftritt:
Delphi-Quellcode:
Die Prüfung auf decimal>0 ist deshalb vorhanden, weil sonst mehrere Kommata pro zahl vorkommen dürften, was natürlich sinnlos wäre. Lange Rede kurz: Der Algorithmus geht von einem mit 0 initialisierten Wert aus.
'.': if decimal>0 then
raise Exception.Create('invalid decimal') else decimal := 10; Da hättest du mit ein paar Sekunden Analyse aber auch drauf kommen können... |
AW: Parser Kommarechnung
Zitat:
Delphi-Narr, Du hast Dir den Spass genommen, das alleine rauszufinden. So geht das aber nunmal beim Programmieren: Man muss selbst Dreck fressen und rumheulen, bis man von selbst auf die Lösung kommt. Nur so wird man wirklich gut. Nächstes mal probierst Du selbst rum und versuchst, das Problem zu verstehen, ok? ;-) |
AW: Parser Kommarechnung
Ich habe das auch gemacht (also decimal:=0), klappt aber nicht richtig (rechnet falsch)...
Bei 1,1 * 9 kommt nicht 9,9 raus sondern 0,4 oder so etwas... Ich habe mal einen komplett neuen geschrieben, der klappt jetzt auch... Der schreibt den Term nach Zahlen und Operatoren sortiert in eine Liste, fügt oder löscht falsche Rechenzeichen (hinzu), geht dann den Term von HINTEN durch und sucht nach Klammern, löst diese auf. Dann normal nach Potemzen suchen und auflösen und dann Punkt vor Strich. Ich hätte mir zwar das normale auflösen (nach den Klammern) sparen können, indem ich einfach Klammern aufrufe, aber aus Testzwecken habe ich das stehen lassen (um die Zehnmilliarden Fehler beim durchgehen zu umgehen). Hier mein Ergebnis:
Delphi-Quellcode:
Viele Grüße und viel Spaß damit :D
function TermTOFloat(Term:string):real;
function Klammern(T:string):string; Var SL: TStringList; i: Integer; S: string; Begin {Falls das erste Zeichen ein Rechenzeichen wie + oder - ist, so wird eine Null eingefügt, um die Rechnung fortzusetzen (Beim Rechnen würde sonst ein Fehler auftreten)} S:=T; case S[1] of '+','-': begin insert('0',S,1); end; {Die Rechenzeichen * und / werden gelöscht} '*','/': begin Delete(S,1,1); end; end; //Rechenzeichen am Ende werden gelöscht case S[Length(S)] of '+','-','*','/': begin Delete(S,Length(S),1); end; end; SL := TStringList.Create; Try Try SL.Add(''); For i := 1 to Length(S) do Case S[i] of '0'..'9', ',', '.': Begin SL[SL.Count - 1] := SL[SL.Count - 1] + S[i]; End; '*', '/', '+', '-','^': Begin If SL[SL.Count - 1] = '' Then Begin // eine leere Zeile kommt vor, wenn mehrere Operatoren // hintereinander liegen, z.B. bei '1*+2' bzw. '1 * +2' SL.Delete(SL.Count - 1); End; SL.Add(S[i]); SL.Add(''); End; ' ': ; // ignoriere Leerzeichen Else Raise Exception.CreateFmt('Ungültiges Zeichen "%s" gefunden.', [S[i]]); End; {Ab hier wird gerechnet} begin begin i:=0; while i < SL.Count do begin if SL[i]='^' then begin SL[i-1]:=(FloatToStr(Power(StrToFloat(SL[i-1]),StrToFloat(SL[i+1])))); {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???} SL.Delete(i); {Jetzt soll die Zeile des Operators gelöscht werden} SL.Delete(i);{und die der zweiten Zahl} {etwas wurde gefunden - fange von vorn an} i := i-1; end else i := i + 1; end; i:=0; while i < SL.Count do begin if SL[i]='*' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])*StrToFloat(SL[i+1]))); {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???} SL.Delete(i); {Jetzt soll die Zeile des Operators gelöscht werden} SL.Delete(i);{und die der zweiten Zahl} {etwas wurde gefunden - fange von vorn an} i := i-1; end else if SL[i]='/' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])/StrToFloat(SL[i+1]))); SL.Delete(i); SL.Delete(i); i := i-1; end else i := i + 1; end; i:=0; while i < SL.Count do begin if SL[i]='+' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])+StrToFloat(SL[i+1]))); {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???} SL.Delete(i); {Jetzt soll die Zeile des Operators gelöscht werden} SL.Delete(i);{und die der zweiten Zahl} {etwas wurde gefunden - fange von vorn an} i := i-1; end else if SL[i]='-' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])-StrToFloat(SL[i+1]))); SL.Delete(i); SL.Delete(i); i := i-1; end else i := i + 1; end; end; end; Finally Result := SL[0]; end; Finally SL.Free; End; end; Var SL: TStringList; i,p,j,a: Integer; S,tmp: string; Begin Result:=0; {Falls das erste Zeichen ein Rechenzeichen wie + oder - ist, so wird eine Null eingefügt, um die Rechnung fortzusetzen (Beim Rechnen würde sonst ein Fehler auftreten)} S:=Term; case S[1] of '+','-': begin insert('0',S,1); end; {Die Rechenzeichen * und / werden gelöscht} '*','/': begin Delete(S,1,1); end; end; //Rechenzeichen am Ende werden gelöscht case S[Length(S)] of '+','-','*','/': begin Delete(S,Length(S),1); end; end; //Klammern durchsuchen und prüfen, ob ein Rechenzeichen davor steht, sonst * einfügen for i:=2 to Length(S) do begin if (S[i]='(') and (pos(S[i-1],'+-*/')=0) then insert('*',S,i); end; SL := TStringList.Create; Try Try SL.Add(''); For i := 1 to Length(S) do Case S[i] of '0'..'9', ',', '.': Begin SL[SL.Count - 1] := SL[SL.Count - 1] + S[i]; End; '*', '/', '+', '-','^','(',')': Begin If SL[SL.Count - 1] = '' Then Begin // eine leere Zeile kommt vor, wenn mehrere Operatoren // hintereinander liegen, z.B. bei '1*+2' bzw. '1 * +2' SL.Delete(SL.Count - 1); End; SL.Add(S[i]); SL.Add(''); End; ' ': ; // ignoriere Leerzeichen Else Raise Exception.CreateFmt('Ungültiges Zeichen "%s" gefunden.', [S[i]]); End; //Klammern suchen und auflösen i:=SL.count-1; while i>=0 do begin if SL[i]='(' then begin p:=i; tmp:=''; j:=p+1; while (SL[j]<>')') do begin tmp:=tmp+SL[j]; j:=j+1; if j=SL.count then break; end; if j=SL.count then j:=j-1; tmp:=Klammern(tmp); SL[p]:=tmp; //Löschen a:=p+1; for p:=P+1 to j do begin SL.Delete(a); end; end; i:=i-1; end; tmp:=''; for i:=0 to SL.count-1 do tmp:=tmp+SL[i]; {Ab hier wird gerechnet} begin begin i:=0; while i < SL.Count do begin if SL[i]='^' then begin SL[i-1]:=(FloatToStr(Power(StrToFloat(SL[i-1]),StrToFloat(SL[i+1])))); {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???} SL.Delete(i); {Jetzt soll die Zeile des Operators gelöscht werden} SL.Delete(i);{und die der zweiten Zahl} {etwas wurde gefunden - fange von vorn an} i := i-1; end else i := i + 1; end; i:=0; while i < SL.Count do begin if SL[i]='*' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])*StrToFloat(SL[i+1]))); {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???} SL.Delete(i); {Jetzt soll die Zeile des Operators gelöscht werden} SL.Delete(i);{und die der zweiten Zahl} {etwas wurde gefunden - fange von vorn an} i := i-1; end else if SL[i]='/' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])/StrToFloat(SL[i+1]))); SL.Delete(i); SL.Delete(i); i := i-1; end else i := i + 1; end; i:=0; while i < SL.Count do begin if SL[i]='+' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])+StrToFloat(SL[i+1]))); {Zahl vor und nach dem * werden multipliziert und in die Zeile der ersten Zahl geschrieben???} SL.Delete(i); {Jetzt soll die Zeile des Operators gelöscht werden} SL.Delete(i);{und die der zweiten Zahl} {etwas wurde gefunden - fange von vorn an} i := i-1; end else if SL[i]='-' then begin SL[i-1]:=(FloatToStr(StrToFloat(SL[i-1])-StrToFloat(SL[i+1]))); SL.Delete(i); SL.Delete(i); i := i-1; end else i := i + 1; end; end; end; Finally tmp:=SL[0]; Try Result := StrToFloat(SL[0]); except Result:=0; end; end; Finally SL.Free; End; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:45 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz