Zum Inhalt springen

Problem mit simplem MathParser


Devilmarkus

Empfohlene Beiträge

Hallo,

ich habe folgendes Problem:

Ich benötige für simple Rechenaufgaben einen MathParser.

Bevor Ihr nun alle postet:"Google ist Dein Freund":

Ich habe mir einen Wolf gegoogelt.

Leider habe ich nur einen Ansatzweise geeigneten Parser gefunden und ihn für meine Bedürfnisse angepasst.

(Ich möchte A: Keine Shareware verwenden und B: Keine neue Bibliothek einsetzen)

Hier nun mein modifizierter Parser:

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

//package jemu.util.ass;


//import jemu.core.Util;


public class MathParser {


    private static final char[] validOperators = {'/', '*', '+', '-', '^'};


    private MathParser() {

    }


    public static int evaluate(String leftSide, char oper, String rightSide)

            throws IllegalArgumentException {

//        leftSide = Z80Assembler.setLabels(leftSide);

//        leftSide = Z80Assembler.putEQU(leftSide);

//        rightSide = Z80Assembler.setLabels(rightSide);

//        rightSide = Z80Assembler.putEQU(rightSide);

//        leftSide.replace("#", "&");

//        leftSide.replace("$", "&");

//        rightSide.replace("#", "&");

//        rightSide.replace("$", "&");

//        if (leftSide.startsWith("&")) {

//            try {

//                leftSide = "" + Util.hexValue(leftSide.replace("&", ""));

//            } catch (Exception e) {

//            }

//        }

//        if (rightSide.startsWith("&")) {

//            try {

//                rightSide = "" + Util.hexValue(rightSide.replace("&", ""));

//            } catch (Exception e) {

//            }

//        }

//        System.out.println("Evaluating: " + leftSide +  " (" + oper + ") " + rightSide);

        int total = 0;

        int leftResult = 0;

        int rightResult = 0;


        int operatorLoc = findLastOperatorLocation(leftSide);

        if (operatorLoc > 0 && operatorLoc < leftSide.length() - 1) {

            leftResult = evaluate(leftSide.substring(0, operatorLoc),

                    leftSide.charAt(operatorLoc),

                    leftSide.substring(operatorLoc + 1, leftSide.length()));

        } else {

            try {

                leftResult = Integer.parseInt(leftSide);

            } catch (Exception e) {

                throw new IllegalArgumentException("Invalid value found in portion of equation: " + leftSide);

            }

        }


        operatorLoc = findOperatorLocation(rightSide);

        if (operatorLoc > 0 && operatorLoc < rightSide.length() - 1) {

            rightResult = evaluate(rightSide.substring(0, operatorLoc),

                    rightSide.charAt(operatorLoc),

                    rightSide.substring(operatorLoc + 1, rightSide.length()));

        } else {

            try {

                rightResult = Integer.parseInt(rightSide);

            } catch (Exception e) {

                throw new IllegalArgumentException("Invalid value found in portion of equation: " + rightSide);

            }

        }


//        System.out.println("Getting result of: " + leftResult + " " + oper + " " + rightResult);

        switch (oper) {

            case '/':

                total = leftResult / rightResult;

                break;

            case '*':

                total = leftResult * rightResult;

                break;

            case '+':

                total = leftResult + rightResult;

                break;

            case '-':

                total = leftResult - rightResult;

                break;

            case '^':

                total = (int)Math.pow(leftResult, rightResult);

                break;

            default:

                throw new IllegalArgumentException("Unknown operator.");

        }

//        System.out.println("Returning a result of: " + total);

        return total;

    }


    private static int findOperatorLocation(String string) {

        int index = -1;

        for (int i = validOperators.length - 1; i >= 0; i--) {

            index = string.indexOf(validOperators[i]);

            if (index >= 0) {

                return index;

            }

        }

        return index;

    }


    private static int findLastOperatorLocation(String string) {

        int index = -1;

        for (int i = validOperators.length - 1; i >= 0; i--) {

            index = string.lastIndexOf(validOperators[i]);

            if (index >= 0) {

                return index;

            }

        }

        return index;

    }


    public static int processEquation(String equation)

            throws IllegalArgumentException {

        return evaluate("0", '+', equation);

    }


    public static String parseMath(String input) {

        try {

            int result = processEquation(input);

            return "" + result;

        } catch (IllegalArgumentException iae) {

        }

        return null;

    }


    public static int parseMaths(String input) {

        try {

            int result = processEquation(input);

            return result;

        } catch (IllegalArgumentException iae) {

        }

        return 0;

    }


    public static void main(String[] args) {

        String usage = "Usage: java MathParser equation\nWhere equation is a series" + " of integers separated by valid operators (+,-,/,*)";


        if (args.length < 1 || args[0].length() == 0) {

            System.out.println(usage);

            String calc = "10+20-4";

            int result = MathParser.processEquation(calc);

            System.out.println("The result of your equation (" + calc + ") should be: 26 is: " + result);

            calc = "20-10+4";

            result = MathParser.processEquation(calc);

            System.out.println("The result of your equation (" + calc + ") should be: 14 is: " + result);

            calc = "20-2+4*4";

            result = MathParser.processEquation(calc);

            System.out.println("The result of your equation (" + calc + ") should be: 34 is: " + result);

            calc = "3^13";

            result = MathParser.processEquation(calc);

            System.out.println("The result of your equation (" + calc + ") should be: 1594323 is: " + result);

        } else {

            String equation = args[0];

            try {

                int result = MathParser.processEquation(equation);

                System.out.println("The result of your equation (" + equation + ") is: " + result);

            } catch (IllegalArgumentException iae) {

                System.out.println(iae.getMessage() + "\n" + usage);

            }

        }

    }

}


Das Problem ist:

Er soll einfach nur simpel rechnen, nichts aussergewöhnliches.

(Klammern, andere Logarithmische Funktionen werden nicht benötigt)

Nur ist das Resultat nicht immer wie erwünscht:

The result of your equation (10+20-4) should be: 26 is: 26

The result of your equation (20-10+4) should be: 14 is: 6

The result of your equation (20-2+4*4) should be: 34 is: 2

The result of your equation (3^13) should be: 1594323 is: 1594323

Wenn jemand einen Plan hat, dieses hier zu ändern, wäre ich dafür sehr dankbar!

Gruss,

Markus

P.s: Ich habe einige "Programmspezifischen" Sachen weg markiert, damit jeder diesen Code kompilieren kann.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Code ist extrem schwer zu lesen. Gerade bei solchen Problemen arbeitet man mit einer Grammatik, die sicherstellt, dass der eingegebene Ausdruck überhaupt korrekt ist. Ich vermute mal, dass der Ausdruck nicht passend geparst wird

Der Ansatz hier ist die Polnische Notation ? Wikipedia Die Gleichung wird als Baum dargestellt und zur Berechnung einfach nur traversiert, wobei an jedem Knoten die Berechnung stattfindet und in den Blättern die Werte stehen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Der Ansatz hier ist die Polnische Notation ? Wikipedia Die Gleichung wird als Baum dargestellt und zur Berechnung einfach nur traversiert, wobei an jedem Knoten die Berechnung stattfindet und in den Blättern die Werte stehen.

In der Polnischen Notation werden die Operatoren VOR die Ziffern geschrieben, wie es aussieht.

Ich benötige sie aber auch mittendrin...

Ich weiss, dass der Code schwer zu verstehen/lesen ist.

Mein Kopf raucht auch schon.

Mir würde folgendes eventuell helfen:

Beispiel:

10+20+3*5

Wenn man nun die 3*5 an den Anfang setzen würde (Also die letzte Berechnung) dann würde das gehen:

3*5+10+20 würde mir mein erwünschtes Ergebnis liefern.

Irgendeine Idee, wie man also die letzte Operation abschneiden kann und der Aufgabe vorne an stellen kann?

Begonnen habe ich:


    public static String parseMath(String input) {

        input = checkInput(input);

        try {

            int result = processEquation(input);

            return "" + result;

        } catch (IllegalArgumentException iae) {

        }

        return null;

    }


    public static String checkInput(String inp) {

        int ops = 0;

        char[] lastop = {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};

        for (int i = 0; i < inp.length(); i++) {

            for (int p = 0; p < validOperators.length; p++) {

                if (inp.charAt(i) == (char)validOperators[p]){

                    ops++;

                    lastop[ops] = inp.charAt(i);

                    break;

                }

            }

        }

        String firstop = ""+lastop[ops-1];

        if (ops > 1){

            String newString ="";

        }

        System.out.println(ops +" operations. Last operation was:"+lastop[ops-1]);

        return inp;

    }

Dieser Code soll also die letze Rechenoperation vorne anstellen...

Nur wie? *Kopfrauchtwiedoof*

Link zu diesem Kommentar
Auf anderen Seiten teilen

In der Polnischen Notation werden die Operatoren VOR die Ziffern geschrieben, wie es aussieht.

Ich benötige sie aber auch mittendrin...

Ich frage mich gerade, warum es überhaupt solche Notationen gibt?

Ich empfehle einmal ganz dringend, dass Du Dir die Grundlagen der Informatik (Datenstrukturen und Algorithmen) anschaust, denn es ist schon sinnvoll bekannte Strukturen zu nutzen.

Außerdem sehe ich überhaupt keinen fachlichen Grund die polnische Notation nicht einzusetzen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Nimm doch mal nen Taschenrechner und reche es selbst nach, wenn ich mich nicht vertippt habe, müsste dort ~1,34035788489 heraus kommen.

Ja das kommt heraus, wenn man mit Radiant rechnet.

Kleine Änderung in meinem Parser:

Input:2^sin(5^(3+5))

Result = 1.3403513252683301

            if (fun.equals("sin")) {

                expression = transformMinus("(" + Math.sin(calculatePostFix(generatePostFix(expression))* 0.0174532925) + ")");

            } else {

                if (fun.equals("cos")) {

                    expression = transformMinus("(" + Math.cos(calculatePostFix(generatePostFix(expression))* 0.0174532925) + ")");

                } else {

                    if (fun.equals("tan")) {

                        expression = transformMinus("(" + Math.tan(calculatePostFix(generatePostFix(expression))* 0.0174532925) + ")");

                    }

                }

            }

* 0.0174532925 um in Radiant umzurechnen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

bin jetzt nicht so in dem Themengebiet deines Projektes drin, aber ist es nicht sehr unschön, mit fixen Werten Umrechungen durchzuführen? ich gehe jetzt einfach mal davon aus, dass dein ergebnis eine geringe Abweichung gegenüber meinem hat, weil deine hard-codierte Zahl nicht so genau ist wie die, die mein windows calc benutzt

Link zu diesem Kommentar
Auf anderen Seiten teilen

bin jetzt nicht so in dem Themengebiet deines Projektes drin, aber ist es nicht sehr unschön, mit fixen Werten Umrechungen durchzuführen? ich gehe jetzt einfach mal davon aus, dass dein ergebnis eine geringe Abweichung gegenüber meinem hat, weil deine hard-codierte Zahl nicht so genau ist wie die, die mein windows calc benutzt

Ich hab es geändert.

Du hattest Recht! Der Wert war zu ungenau.

Input:2^sin(5^(3+5))

Result = 1.3403578848874218

Sollte nun akkurat sein.

            if (fun.equals("sin")) {

                expression = transformMinus("(" + Math.sin(Math.toRadians(calculatePostFix(generatePostFix(expression)))) + ")");

            } else {

                if (fun.equals("cos")) {

                    expression = transformMinus("(" + Math.cos(Math.toRadians(calculatePostFix(generatePostFix(expression)))) + ")");

                } else {

                    if (fun.equals("tan")) {

                        expression = transformMinus("(" + Math.tan(Math.toRadians(calculatePostFix(generatePostFix(expression)))) + ")");

                    }

                }

            }

Link zu diesem Kommentar
Auf anderen Seiten teilen

dann sind wir schonmal nen Schritt weiter. Nun zu deinen Ergebnissen von oben:

The result of your equation (10+20-4) should be: 26 is: 26
scheint ja ok zu sein
The result of your equation (3^13) should be: 1594323 is: 1594323 
scheint auch ok zu sein

The result of your equation (20-10+4) should be: 14 is: 6
hmmmm....

The result of your equation (20-2+4*4) should be: 34 is: 2

und nochmal hmmmm.....

Sieht mir beiden so aus, als ob er die rechenzeichen nicht richtig auswertet.

1.: er rechnet 20-10-4

2.: er rechnet 20-(4*4 + 2)

hab jetzt nicht die möglichkeit gehabt, mir deinen code genau anzuschauen. aber es sieht stark nach einem falschen evaluierung der zeichen bzw der reihenfolge aus

Link zu diesem Kommentar
Auf anderen Seiten teilen

dann sind wir schonmal nen Schritt weiter. Nun zu deinen Ergebnissen von oben:

....

hab jetzt nicht die möglichkeit gehabt, mir deinen code genau anzuschauen. aber es sieht stark nach einem falschen evaluierung der zeichen bzw der reihenfolge aus

Das Problem hat sich gelöst.

Ich hatte vorher falsch geparst.

Der jetzige Parser tut wie er soll.

Ich kann sogar die Punkt-vor-Strich Rechnung abschalten.

Das brauche ich für meinen eigentlichen Programmteil:

Z80 Assembler.

Der benötigt diesen Parser.

Beispiel:

DS &3318-&3301+4*14

DS bedeutet: er soll eine bestimmte Anzahl an (in diesem Fall) Nullen aneinanderreihen.

Diese Rechnung ergibt nach "normaler" Regel 79!

Allerdings soll der Assembler kompatibel zu einem 'echten' Z80 Assembler sein und deshalb keine Punkt-vor-Strich Rechnung machen,

Also kommt als Ergebnis 378 heraus...

(Macht einen gewaltigen Unterschied, ist aber in diesem Fall das gewünschte Ergebnis)

maxam.png

Bild: Original Maxam-Assembler mit derselben Berechnung

Link zu diesem Kommentar
Auf anderen Seiten teilen

es scheint so als wäre da einProblem mit dem *-Operator nach einem Exponenten

Wie ich schon geschrieben hatte, wird das ein Problem des Parsings sein. Gerade für mathematische Ausdrücke wird ein Baum aufgebaut in dem man den Ausdruck ablegt. Aus der entsprechenden Notation, erst Operator, dann Werte, ist die Verarbeitung nach den mathematischen Gesetzen leicht möglich.

Ist ein klassisches Beispiel der Informatik 1/2 Vorlesung.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ist ja an sich eine feine Sache...

Mir ist aufgefallen, dass

2^(3+1)*2 = Err,

2^(3+1)+2 = 18

2^(3+1)-2 = 14

2^(3+1)/2 = 8

2^2*2 = Err

es scheint so als wäre da einProblem mit dem *-Operator nach einem Exponenten

Sollte nun alles funktionieren.

Übrigens lustig:

In meiner Benachrichtigungsmail wurde die letzte Aufgabe so abgeändert:

2²*2 = Err

Auch dieser Fall sollte jetzt funktionieren.

² und ³ sollten korrekt gelesen werden.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wie ich schon geschrieben hatte, wird das ein Problem des Parsings sein. Gerade für mathematische Ausdrücke wird ein Baum aufgebaut in dem man den Ausdruck ablegt. Aus der entsprechenden Notation, erst Operator, dann Werte, ist die Verarbeitung nach den mathematischen Gesetzen leicht möglich.

Ist ein klassisches Beispiel der Informatik 1/2 Vorlesung.

Das ist mir durchaus bewusst ich wollte mit dem Satz den TE nur behilflich sein die Intention meiner Auflistung leichter zu verstehen und dementsprechend die Stelle des Fehlers im Quellcode leichter zu finden ;)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Das ist mir durchaus bewusst ich wollte mit dem Satz den TE nur behilflich sein die Intention meiner Auflistung leichter zu verstehen und dementsprechend die Stelle des Fehlers im Quellcode leichter zu finden ;)

Danke, das ist auch hilfreicher für mich.

Dinge wie "Aus Dokument soundso oder aus Regel soundso" nützen mir garnichts.

- JavaDocs verstehe ich (noch) nicht.

- Ich habe nie Java-Schulungen mitgemacht.

- Alles, was ich in Java mache, habe ich mir selbst beigebracht (Trial & error) oder hier im Forum erfragt.

Was ist mit ^0.5 ? bzw auch transzendeten Zahlen aus R z.B. ^-Pi

2^-pi ergibt:

0.11331473229676088

2^0.5 ergibt:

1.4142135623730951

^0.5 ergibt:

0.5

(Ob es richtig ist, weiss ich nicht. Bin ein null-Mathematiker)

Bearbeitet von Devilmarkus
Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke, das ist auch hilfreicher für mich.

Dinge wie "Aus Dokument soundso oder aus Regel soundso" nützen mir garnichts.

- JavaDocs verstehe ich (noch) nicht.

- Ich habe nie Java-Schulungen mitgemacht.

- Alles, was ich in Java mache, habe ich mir selbst beigebracht (Trial & error) oder hier im Forum erfragt.

Du scheinst aber gerade in letzterem Punkt nicht immer die Ratschläge zu beherzigen, denn neben der Dokumentation, die es nicht nur Dir leichter macht Deinen Code zu verstehen, kann man auch als Außenstehender versuchen Fehler zu finden.

Auch mein Hinweis zur Polnische Notation würde Dir einiges an Arbeit ersparen, vor allem weil im Wikipedia Artikel folgendes steht:

Im Bereich der Mathematik werden in der üblichen Schreibweise viele Funktionen in polnischer Notation aufgeschrieben, z. B. Sinus („sin 30“) oder Logarithmus („ln 10“). Im Allgemeinen ist in der Mathematik aber eine Infix-Schreibweise üblich, bei der die Operatoren zwischen ihre Argumente geschrieben werden (z. B. „1 + 2“).

Auch der Hinweis auf die Infixnotation wird dort gegeben

(Ob es richtig ist, weiss ich nicht. Bin ein null-Mathematiker)

Wenn Du einen Parser schreibst, der einen mathematischen Ausdruck verarbeitet, dann solltest Du auch validieren können, ob das Ergebnis richtig ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Wenn Du einen Parser schreibst, der einen mathematischen Ausdruck verarbeitet, dann solltest Du auch validieren können, ob das Ergebnis richtig ist.

Ich habe Teile eines vorhandenen Parsers verwendet und an meine Bedürfnisse angepasst / erweitert.

Für das Wesentliche, was er können muss, nämlich genauso rechnen, wie der MAXAM-Z80 Assembler das kann, reicht er völlig aus...

Der "Scientific Calculator" ist eigentlich nur ein Überbleibsel einer simplen Eingabe-GUI womit ich den Parser für meine Bedürfnisse getestet habe.

(Ich war nur zu Faul, den wieder zu löschen)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Dein Kommentar

Du kannst jetzt schreiben und Dich später registrieren. Wenn Du ein Konto hast, melde Dich jetzt an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

Fachinformatiker.de, 2024 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...