Probleme mit dem Taschenrechner-Programm



  • Hallo,

    ich soll ein Taschenrechnerprogramm in ansi c schreiben. Ich habe das Programm schon soweit fertig.
    eizige Probleme:
    - wenn ich Flieskommazahlen eingebe z.B. 3.1 +1.2 dann rechnet mein rechner 3+1 unde 1+2 getrennt und gibt diese ergebnisse auch getrennt aus.
    - mein zweites problem die Fehlerabfrage bei fehlenden Klammern geht noch nicht richtig. wenn ich z.B. eine Klammer vergesse einzugeben dann soll ein Fehler-Text ausgegeben werden. Wenn ich aber nur eine Klammer zu ")" setze dann kommt keine Fehlermeldung sondern nur das Ergebnis 0.

    Vielen Dank für die Hilfe.

    Hier der code:

    #include <stdio.h>
    #include <stdlib.h>
    
    char error='0';
    int klauf=0, klzu=0;
    
    double expression (char *ch);
    double term       (char *ch);
    double factor     (char *ch);
    double number     (char *ch);
    double digits     (char *ch);
    int    digit      (char *ch);
    
    double expression(char *ch)
    {
      double d;
      switch (*ch)
      {
        case'+': *ch = getchar(); d = term(ch); break;
        case'-': *ch = getchar(); d = - term(ch); break;
        default: d = term(ch);
      }
      while ( ( *ch=='+' ) || ( *ch=='-') )
      {
        switch (*ch)
        {
          case'+': *ch = getchar(); d+= term(ch); break;
          case'-': *ch = getchar(); d-= term(ch);
        }
      }
    
      return (d);
    }/*expression*/
    
    double term (char *ch)
    {
      double d;
      d = factor(ch);
      while((*ch=='*')||(*ch=='/'))
      {
        switch (*ch)
        {
          case'*': *ch = getchar(); d*= factor(ch); break;
          case'/': *ch = getchar(); d/= factor(ch);
        }
      }
    
      return (d);
    }/*term*/
    
    double factor (char *ch)
    {
      double d;
      switch (*ch)
      {
        case'(': klauf++; *ch = getchar(); d= expression(ch);
        case')': while(*ch==')'){klzu++; *ch = getchar();
                 if (*ch==')') error='1';} break;
        default: d = number(ch);
      }
    
      return(d);
    }/*factor*/
    
    double number (char *ch)
    {
      double d;
      switch (*ch)
      {
        case'+': *ch = getchar(); d = digits(ch); break;
        case'-': *ch = getchar(); d = -digits(ch); break;
        default: d = digits(ch);
      }
    
      return (d);
    }/*number*/
    
    double digits (char *ch)
    {
      double d=0;
     /* int check=0;*/
      if (*ch=='p')
      {
        *ch = getchar();
        if (*ch == 'i')
        {
          d=3.14159265359;
          *ch = getchar();
          if ((*ch >='0')&&(*ch <='9')) d*=digits(ch);
        }
      }
      while ((*ch >='0')&&(*ch <='9'))
      {
        d*=10;
        d+=digit(ch);
      }
      if (*ch=='p')
      {
        *ch = getchar();
        if (*ch == 'i')
        {
          d*=3.14159265359;
          *ch = getchar();
          if (((*ch >='0')&&(*ch <='9'))||(*ch=='p')) d*=digits(ch);
        }
      }
    
      return(d);
    }/*digits*/
    
    int digit (char *ch)
    {
      int d;
      d=(*ch-'0');
      *ch = getchar();
      return(d);
    
    }/*digit*/
    
    int main(void)
    {
      double res=0;
      char start;
      printf("+--------------------------------------------------------------+\n");
      printf("|.......................Calculator.............................|\n");
      printf("|.......This calculator can use (, ), +, -, *, /...............|\n");
      printf("|..................__..........................................|\n");
      printf("|.......use pi for ||..........................................|\n");
      printf("|.......press 'q' to quit......................................|\n");
      printf("+--------------------------------------------------------------+\n>");
      while (start!='q')
      {
        start = getchar();
        printf("kann losgehen...\n");
        res = expression(&start);
        if (klauf!=klzu) error = '1';
        klauf = klzu = 0;
        switch (error)
        {
          case '0' : printf("\n= %f \n\n>",res);break;
          case '1' : printf("\n Error 1:\n ')' does not match '('\n\n>");
                     error='0';break;
        }
      }
      getchar();
      return 0;
    }/*main*/
    


  • Aeon schrieb:

    - wenn ich Flieskommazahlen eingebe z.B. 3.1 +1.2 dann rechnet mein rechner 3+1 unde 1+2 getrennt und gibt diese ergebnisse auch getrennt aus.

    wenn ich die ausgabe richtig deute, rechnet dein programm die folgenden drei rechnungen:
    3=
    1+1=
    2=

    das liegt vermutlich daran, dass der punkt von der getchar-funktion anders interpretiert wird, als du dir es wünschst. schau mal, wie die genau funktioniert (ich weiß es jetzt auch nicht genau), und ob du mit scanf nicht vielleicht besser bedient bist. da müsstest du dann eben erst den kompletten ausdruck einlesen und kannst den dann auch zeichen für zeichen durchgehen. (aber auch da musst du aufpassen, was scanf zurückgibt. ->leerzeiche, punkt, enter, ...)



  • Deine digits-Routine ist überhaupt nicht dafür vorgesehen, Fließkommazahlen einzulesen.



  • Also wenn du es nicht unbedingt so machen musst, dann würde ich an deiner Stelle erst die ganze Zeile einlesen und dann nach den Rechenoperationen parsen. Da kannst du dann auch besser auf bestimmte Regeln aufpassen: Klammer vor Punkt vor Strich 😉



  • AJ: Worauf spielst du an? Das ist ein Parser, und er beherrscht Operatorrangfolgen.



  • @bashar
    Ok, hast recht. Hab mir das ganze nicht so genau angeschaut. Finde den Aufbau nur recht verwirrend, besonders mit den ganzen getchar()s in den Funktionen. Naja hauptsache es funktioniert.



  • Das mit dem getchar liegt wohl daran, dass Scanner und Parser in eins «vermanscht» sind.



  • AJ schrieb:

    Also wenn du es nicht unbedingt so machen musst, dann würde ich an deiner Stelle erst die ganze Zeile.....

    @AJ Ja ich muss das so machen wir haben Das Programm in EBNF-Diagramm-Form bekommen und sollen es in c programmieren.
    Wer sich das Blatt ansehen will hier die url dazu: http://www.ti3.tu-harburg.de/ProgMetho/aufgaben/blatt10.pdf

    Noch eine Frage, da ich noch ein richtiger anfänger im programmieren bin
    was genau bedeutet parsen?

    danke für die Hilfe



  • Aeon: Die Grammatik in der Aufgabenstellung sieht keine Fließlkommazahlen vor, also kann der Rechner das logischerweise auch nicht. Eine Möglichkeit zur Fehlererkennung wäre, nach Erkennung des Startsymbols zu prüfen, ob die gesamte Eingabe verarbeitet wurde. Wenn etwas übrig geblieben ist, liegt ein Syntaxfehler vor.

    Parsen heißt Syntaxanalyse, d.h. nach bestimmten Regeln (Grammatik) Struktur in einer linearen Sequenz von Symbolen zu finden. Z.B. ist "(a+b)*2" für sich nur eine Ansammlung aus 7 Zeichen. Ein Parser erkennt, dass das eine Addition zweier Variablen ist, deren Ergebnis wiederum mit einer Zahl multipliziert wird.



  • @Bashar ah vielen dank wieder was gelernt.

    nun ich habe noch eine Frage: ich habe versucht nun noch die sinus-fkt in den taschenrechner einzubauen. leider hatte ich bis jetzt nicht den erfolgt den ich wollte. wenn ich die sin-fct einbaue hat das programm Problem das argument in den sinusklammer eindeutug zu erkennen.

    naja den sinus alleine von einem wert z.b pi/2 zu berechnen klappt.

    hat jemand vielleicht eine Idee wie ich die sin-fct einbauen kann?

    danke



  • @aeon
    Also ich habe die Aufgabenstellung nicht so verstanden, dass du den Ausdruck nicht erst komplett einlesen darfst. So wie ich das verstanden habe, sollst du sogar zuerst die komplette Zeile einlesen.

    Verwendest du die sin()-Funktion von C? Dann musst du aufpassen, dass der Parameter im Bogenmaß angegeben werden muss. Oder hast du ein Problem den Wert zwischen den Klammern in der Eingabezeile rauszulesen? Dann wär ein bisschen Quellcode nicht schlecht 😉 (bitte aber nur den Teil mit deiner Sinusbehandlung).



  • Ich würde mir einen schönen Platz in der Grammatik suchen, wo ich Funktionen unterbringen kann. Die <factor>-Produktion zu erweitern wär mein erster Gedanke.

    <factor> ::= ( <expression> ) | <number> | <function> ( <expression> ) ;
    <function> :: = sin | cos | ln etc.
    


  • Wenn du morgen erst dein Testat hast, solltest du dir nicht alzu viel
    Sorgen um das Klammernzählen machen. Durch die ganzen getchars in dem Programm
    aus der Vorlsung kann man das gar nicht so leicht in "hübscher" Art und Weise
    einbauen. Hat selbst unsere Übungsleiterin nicht auf anhieb geschafft: Sie
    musste die Musterlösung korriegieren 🙂 Bei der Aufgabe sind die Prüfer ein
    bissl großzügig.
    Hatte mein Testat schon Mittwoch 😉

    Hab das sin/exp - Problem quasi so gelöst wie Bashar.

    Edit: Ich würd mir noch mal deine Funktion didgits anschauen. Sieht so aus,
    als hättest du da einigen redundanten Code drin, da du ja entweder eine
    ganze Zahl oder pi einliest - andere Fälle kannst du ignorieren, da nur die
    Klammern auf Fehler geprüft werden sollen.



  • @AJ wir sollen eine eigene sin()-fkt benutzen aber es macht keinen unterschied wir benutzen an unserer uni standartmäßig das bogenmaß, es steht dabei wenn wir das gradmaß benutzen sollen.

    Wenn du interesse hast han selbst gebauten sinusfkt, ich habe 3 versionen im angebot: eine approximation mit dem horner-schema, mit der sinusreihe und noch eine version die zusätzlich mit neville-interpolation einen genaueren wert berechnet.

    Vielen dank für eure Hilfe. Also heute haben wir das programm abgeben müssen und einer mit dem ich zusammenabgegeben habe hat ein funktionierendes programm hinbekommen, unserem tutor hat das programm sogar noch besser gefallen als die vorgabe die wir hatten. er hat ein wenig überlegt und hat sich dann für eine pointer of pointer methode entschieden, die war dann einfacher zu handhaben.

    den einzigen fehler den das programm noch prodziert ist das mit den klammern.
    Wenn man zwei klammer in dieser -> )( Reihenfolge eingibt findet das programm keinen fehler, da es nur die anzahl der offenen klammern incrementeiert bzw wieder decrementiert wird und dann geprüfft ob die anzahl == 0 ist.

    hier erstmal das (fast)Feritge taschenrechner programm:

    #include <stdio.h>
    #include <math.h>
    #define PI	3.14159265358979323846
    
    double expression(char **ch);/* pointer auf pointer, um in den Funktionen den pointer auf den eingabestring inkrementieren zu koennen */
    double term(char **ch);
    double factor(char **ch);
    double number(char **ch);
    
    /******Sinus********************************************************************/
    double sinus (double x);
    {
        double Wert,Summand,Wert_alt;
        int m,n,a=1;
    
        if (x<0)/*für alle negativen x wird a negativ und x positiv*/
         {
         a=-1;
         x=-x;
         }
    
        x=fmod(x,(2*PI));/*x wird auf das Intervall [0,2PI] Reduziert*/
    
        if (x>PI)/*für x>PI a wird mit -1 multipliziert und x wird am Wendepunkt gespiegelt*/
         {
         a=-a;
         x=(2*PI)-x;
         }
    
        if (x>(PI/2))/*a bleibt unverändert und x auf das Intervall [0,PI/2] geschickt*/
         {
         x=(PI-x);
         }
    
        m=0;
        n=1;
        Wert=x;/*Initialisierung der Variablen*/
        Summand=x;
        Wert_alt=x+1;
    
        while(Wert_alt!=Wert)/*Beginn der while- Schleife mit Abbruchbedingung wenn der alte Wert=Summe Alterwert& neues Glied*/
          {
          m=m+2;/*m&n jeweils+2 ergeben die hinzukommenden Glieder der Fakultäten*/
          n=n+2;
          Summand=-Summand*x*x/m/n;/*Festlegung des neuen Summanten der alterniert*/
          Wert_alt=Wert;/*wertalt=Wert neu*/
          Wert=Wert+Summand;/*Wert= Summe des alten Wertes und des Gliedes*/
          }
        return(a*Wert);
        }
    
    double normalize (double x) /* normalize the x value to a value between 0 and Pi/2 to improve results*/
    {
    	if (x < 0) x *= -1.0; /* same as abs(x) */
    	x = fmod(x, 2*PI); /* as sin(x) has a period of 2Pi the modulo operation doesnt change the result  */
    
    	if (x <= (PI/2)) return x; /* these calculations are possible because sin(x) is periodical */
    	if (x > PI) return ((-2*PI) + x);
    	if (x <= PI) return (PI - x);
    
    	return x;
    }
    
    /******Expression***************************************************************/
    /* zustaendig fuer summen und differenzen, alles andere wird an term weitergereicht*/
    double expression(char **ch)
    {
      double d = 0.0;/* variable fuer den Rueckgabewert */
      switch (**ch)
       {/* erster term :  + oder -, jenachdem term ergebnis interpretieren */
        case '+':  (*ch)++; d=  term(ch); break;/* (*ch)++; = geht ein zeichen weiter */
        case '-':  (*ch)++; d= -term(ch); break;
        default :  d= term(ch); break;
       }
      while (1)
        switch (**ch)
    		{/* dasselbe gilt fuer alle weiteren terme, bis keiner mehr da ist*/
          case '+':  (*ch)++; d= d + term(ch); break;
          case '-':  (*ch)++; d= d - term(ch); break;
          default :  return(d);
        }
    } /* expression */
    /******Term*********************************************************************/
    /* hier werden produkte und quotienten verarbeitet und an factor uebergeben*/
    double term(char **ch)
    {
      double d = factor(ch);/* ersten teil sofort an factor weiterreichen */
      while (1)
      switch (**ch)
       {/* folgen weitere teile (mit * oder /), entsprechend zu d */
    	/* dazurechnen und rest an factor uebergeben */
          case '*':  (*ch)++; d = d * factor(ch); break;
          case '/':  (*ch)++; d = d / factor(ch); break;
          default :  return(d);
       }
    } /* term */
    
    /******Faktor*******************************************************************/
    /* ueberpruefen, ob ein weiterer ausdruck oder nur eine zahl folgt */
    double factor(char **ch)
    {
      double d = 0.0;
      switch (**ch)
       {
        case '(':  (*ch)++; d = expression(ch); (*ch)++; break;/* weiterer ausdruck -> an expression uebergeben*/
        case 's': /* sinus -> folgender ausdruck an expression */
    			if ((*(++(*ch)) == 'i') && (*(++(*ch)) == 'n'))
    			{
    				(*ch)++;/* 2 zeichen weiter ( fuer 'n' und '(' )*/
    				(*ch)++;
    				d = sinus(expression(ch));
    				(*ch)++;
    			}
    			break;
        case 'c':/* cosinus -> folgender ausdruck an expression */
    			if ((*(++(*ch)) == 'o') && (*(++(*ch)) == 's'))
    			{
    				(*ch)++; /* 2 zeichen weiter ( fuer 's' und '(' )*/
    				(*ch)++;
    				d = sinus(expression(ch)+(PI/2));
    				(*ch)++;
    			}
    			break;
    		case 'e':/* exponentialfkt -> folgender ausdruck an expression */
    			if ((*(++(*ch)) == 'x') && (*(++(*ch)) == 'p'))
    			{
    				(*ch)++;/* 2 zeichen weiter ( fuer 'p' und '(' )*/
    				(*ch)++;
    				d = exp(expression(ch));
    				(*ch)++;
    			}
    			break;
    		default :  d = number(ch);/* der einfachste fall, zahl folgt -> an number */
    	}
    	return(d);
    } /* factor */
    
    /******Number*******************************************************************/
    /* liest zahl oder sonderzeichen ein */
    double number(char **ch)
    {
      double s = 1.0, d = 0.0, n = 10.0;
      switch (**ch)/* vorzeichen (signum) der zahl einlesen */
    	{
        case '+':  (*ch)++; break;
        case '-':  (*ch)++; s = -1; break;
      }
    	/* sonderzeichen */
    	if ((**ch == 'p') && (*(++(*ch)) == 'i'))/* auf pi testen */
    	{
    		d = PI;
    		(*ch)++;
    	}
    	else
    	{/* zahl einlesen */
    		while ( ('0'<=**ch) && (**ch<='9') )
    		{/* vor dem punkt */
    			d = 10*d + (**ch-'0');
    			(*ch)++;
    		}
    		if (**ch == '.')
    		{/* nach dem punkt */
    			(*ch)++;
    			while ( ('0'<=**ch) && (**ch<='9') )
    			{
    				d = d + ((**ch-'0') / n);
    				n *= 10;/* funktioniert natuerlich nur fuer begrenzt viele stellen */
    				(*ch)++;
    			}
    		}
    	}
      return(s*d);
    } /* number */
    
    /******Hauptprogramm************************************************************/
    int main(void)
    {
    	char ch[256]={'\0'};/* fuer die eingabe, mit nullen vollschreiben; das eine '\0' braucht z.B. der MS compiler */
    	char *pch;/* pointer auf anfg von ch */
    	int br = 0, n = 0;
    
    	printf("Taschenrechner (Druecke q zum Beenden)\n");
    	pch = ch; /* s.o. */
    	printf(">");/* alternativ auch "C:\>" oder "user@machine:~ >" */
      scanf("%s", ch);
      while (ch[0]!='q')/* mit q beenden */
    	{
    	br = 0;
    	n = 0;
    	while (ch[n] != '\0')/* anz der klammern (brackets) bis zum \0 zaehlen*/
    	{
    		if (ch[n] == '(') br++;/* br wird <0 wenn zuviele ')', sonst >0 d.h. zuviele '(' */
    		if (ch[n] == ')') br--;
    		n++;
    	}
      if (br == 0)/* Klamemr-anzahl-richitg */
    		printf(" = %f\n", expression(&pch) ); /*<--- hier faengt alles an !, pointer auf pointer auf anfg vom eingabefeld */
      else  /* Klammerfehler */
    		printf("error - check brackets. %dx '%c' missing.\n", ((br<0)?-br:br), ((br<0)?'(':')') );
    	pch = ch; /* wieder auf feldanfang, wird ja von den fkt veraendert */
    	printf(">");
    	scanf("%s", ch);
      }
      return 0;
    } /* main */
    


  • Die Sinus-Funktion mit Horner-Schema:

    #include <stdio.h>
    #include <math.h>
    #define pi 3.14159265358979323846
    
    double sinus(double x)
    {
    
      double pi2 = 2 * pi;
      double pi1 = pi;
      double piHalb = 0.5 * pi; 
    
      int sign =1; /*dient zum speichern des Vorzeichens)*/
    
      /*Ändert x, bis x im Intervall [0,pi/2] liegt.  
      Das Vorzeichen wird extern gespeichert*/
      if (x < 0) { x *= -1.0; sign *= -1; }
      if (x > 2 * pi) { x = fmod(x,pi2); }
      if (x > pi1) { x = ( pi2 - x); sign *= -1; }
      if (x > piHalb) { x = pi1 - x; };
    
      /*Berechnung von sin(x)*/
      x= sign*(x*(1-x*x/6*(1-x*x/20*(1-x*x/42))));
    
      return(x);
    }; /*Sinus*/
    

    Hier die Sinus-Fkt mit Sin-Reihen-Approximation

    #include <stdio.h>
    #include <math.h>
    #define pi 3.14159265358979323846;
    
    double sinus(double x)
    {
      double oldres = 2, /*altes Ergebnis*/
             res    = 0, /*neues Ergebnis*/
             add    = 0; /*Einzelsummand aus Sinusreihe*/
      int    sign   = 1; /*dient zum speichern des Vorzeichens vom Ergebniss*/
      long   k      = 1; /*laufvariable*/
    
      double pi2 = 2 * pi;
      double pi1 = pi;
      double piHalb = 0.5 * pi; 
    
      /*Ändert x, bis x im Intervall [0,pi/2] liegt.  
      Das Vorzeichen wird extern gespeichert*/
      if (x < 0){x *= -1.0; sign *= -1;}
      if (x > pi2)x = fmod(x,pi2);
      if (x > pi1) {x = (pi2-x); sign *= -1; }
      if (x > piHalb) {x = pi1 - x;};
    
      /*Berechnung von sin(x)*/
      add = res = x;/*Erstes Glied der Summe(k=0)*/
      while (oldres != res)
      {
        oldres = res;
        add *= -(x*x)/(2*k*(2*k+1));/* (x^(2k+1))/((2k+1)!)ab k=1 aufmultipliziert*/
        k++;
        res += add;
    
      }  
      res *= sign; 
      return(res);
    }; /*sinus*/
    

    Hier Die Sinus-Fkt mit zusätzlich Neville-Interpolation

    /* Fkt berechnet den Sinus mit hilfe von Neville-Interpolation*/
    double sinus (double x, short st)/* x ist x-wert, st: anzahl der Stützwerte*/
    {
      short   a, b; /*Laufvariablen für die for-schleifen*/
      double  X[st],/*Stützwerte-Array*/
              Y[st];/*Stützstellen-Array*/
    
      double pi2 = 2 * pi;
      double pi1 = pi;
      double piHalb = 0.5 * pi; 
    
      int sign =1; /*dient zum speichern des Vorzeichens)*/
    
      /*Ändert x, bis x im Intervall [0,pi/2] liegt.  
      Das Vorzeichen wird extern gespeichert*/
      if (x < 0) { x *= -1.0; sign *= -1; }
      if (x > 2 * pi) { x = fmod(x,pi2); }
      if (x > pi1) { x = ( pi2 - x); sign *= -1; }
      if (x > piHalb) { x = pi1 - x; };
    
      /*Erzeugung von Stützstellen und werten*/
      for ( a=0 ; a<st ; a++ )
      {
        X[a] = a * 0.5 / st * pi;
        Y[a] = SinOld(X[a]); /*SinOld() ist die Sin()-Fkt die den sinus mit
                               der Sinus-Reihe berechnet.*/
      }
    
      /* Interpolation des Wertes*/
      for ( b=1; b<st; b++)
      {
    	 for ( a=0; a<(st-b); a++)
    	 {
    		Y[a] = Y[a+1] + (Y[a+1] - Y[a]) / ((x - X[a]) / (x-X[a+b]) - 1);
    	 }
      }   
      Y[0]*=sign;  
      return(Y[0]);
    }/*Sinus*/
    

    Wer Interesse hat Die Aufgaben-Blätter zu diesen Lösungen findet ihr Hier es sind die Aufgaben 5 und 6:
    http://www.ti3.tu-harburg.de/ProgMetho/



  • )( findest du, indem du brüfst, ob br zwischendruch negativ wird.



  • Danke für die Idee, hab gar net dran gedacht...

    negativ, das einfachste entfällt einem meistens zuerst,



  • Wozu soll das Klammernzählen gut sein? Der Parser wirds schon merken, wenn da was nicht stimmt.



  • @Bashar: Ja der Parser merkt es, gibt aber keine Fehlermeldung zurück sondern nur ein falsches ergebnis.
    Deswegen das klammerzählen, es dient zum abfangen von fehlern und damit niemand mit einem falschen ergebnis weiter rechnet.



  • Dann muss man den Parser halt so schreiben, dass er nen Fehler ausgibt.


Anmelden zum Antworten