kalkulacka
This commit is contained in:
parent
150fccfddc
commit
28a85c8514
18
sk2/Makefile
Normal file
18
sk2/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
CC=gcc
|
||||
CFLAGS=-std=c11 -Wall -Wextra -O2
|
||||
LDFLAGS=-lm
|
||||
|
||||
all: calc
|
||||
|
||||
calc: main.o calculator.o
|
||||
$(CC) $(CFLAGS) -o calc main.o calculator.o $(LDFLAGS)
|
||||
|
||||
main.o: main.c calculator.h
|
||||
$(CC) $(CFLAGS) -c main.c
|
||||
|
||||
calculator.o: calculator.c calculator.h
|
||||
$(CC) $(CFLAGS) -c calculator.c
|
||||
|
||||
clean:
|
||||
rm -f *.o calc
|
||||
|
||||
59
sk2/README.MD
Normal file
59
sk2/README.MD
Normal file
@ -0,0 +1,59 @@
|
||||
# Vedecká kalkulačka Matej Hajduk
|
||||
|
||||
## Zadanie
|
||||
Naprogramovať vedeckú kalkulačku, ktorá vyhodnocuje matematické výrazy v infixnej notácii.
|
||||
Kalkulačka musí podporovať:
|
||||
|
||||
- načítanie a prácu s číslami s presnosťou min. 2 desatinné miesta
|
||||
- sčítanie, odčítanie, násobenie, delenie
|
||||
-zátvorky
|
||||
- funkcie: sinus, cosinus, odmocnina, druhá mocnina, logaritmus
|
||||
|
||||
Príklad:
|
||||
- (2 + 3) * 2 -> 10
|
||||
- (10 * 2) + (6 / 2) -> 23
|
||||
|
||||
|
||||
## Stručný opis funkčnosti
|
||||
Program číta matematické výrazy zo štandardného vstupu (každý výraz na samostatnom riadku).
|
||||
Pre každý výraz vypočíta výsledok a vypíše ho na štandardný výstup.
|
||||
|
||||
-pri chybe vypíše hlášku na stderr
|
||||
-podporuje aj zložitejšie výrazy so zátvorkami
|
||||
-podporuje aj unárne mínus (napr. -3*(2+1))
|
||||
|
||||
## Stručný opis riešenia
|
||||
Riešenie je rozdelené na 3 časti:
|
||||
|
||||
1. Tokenizácia
|
||||
-rozdelenie vstupného reťazca na tokeny (čísla, operátory, funkcie, zátvorky)
|
||||
|
||||
2. Prevod infix -> postfix
|
||||
-použitý algoritmus Shunting-yard
|
||||
-rieši priority operátorov a zátvorky
|
||||
|
||||
3. Vyhodnotenie postfixu
|
||||
-postfixový výraz sa vyhodnotí pomocou zásobníka (stack)
|
||||
-pracuje sa s typom double
|
||||
|
||||
## Podmienky za ktorých program funguje
|
||||
- výrazy musia byť syntakticky správne (napr. správne zátvorky)
|
||||
- podporované operátory: + - * / ^
|
||||
-podporované funkcie: sin(x), cos(x), sqrt(x), log(x), ln(x)
|
||||
- podporované konštanty: pi, e
|
||||
- sin a cos používajú radiány
|
||||
- log(x) je log10(x)
|
||||
- delenie nulou je ošetrené ako chyba
|
||||
-doménové chyby:
|
||||
- sqrt(x) vyžaduje x >= 0
|
||||
- log(x) a ln(x) vyžadujú x > 0
|
||||
|
||||
## Použité zdroje
|
||||
-Matej Hajduk (implementácia)
|
||||
- ChatGPT (GPT-5.2), branch: sk2 (pomoc pri návrhu)
|
||||
- Shunting-yard algoritmus (infix -> postfix)
|
||||
-dokumentácia C:
|
||||
- math.h (sin, cos, sqrt, log, log10, pow)
|
||||
- strtod() (parsovanie čísiel)
|
||||
-geek4geeks
|
||||
|
||||
475
sk2/calculator.c
Normal file
475
sk2/calculator.c
Normal file
@ -0,0 +1,475 @@
|
||||
#include "calculator.h"
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - globalna chyba */
|
||||
/* ---------------------------- */
|
||||
static char g_err[128] = "OK";
|
||||
|
||||
/* - helper: set error */
|
||||
static void set_err(const char *msg) {
|
||||
strncpy(g_err, msg, sizeof(g_err)-1);
|
||||
}
|
||||
|
||||
/* - vrat error text */
|
||||
const char *calc_error(void) {
|
||||
return g_err;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - token typy */
|
||||
/* ---------------------------- */
|
||||
typedef enum {
|
||||
T_NUM, /* - cislo */
|
||||
T_OP, /* - operator */
|
||||
T_FUNC, /* - funkcia */
|
||||
T_LP, /* - ( */
|
||||
T_RP /* - ) */
|
||||
} TType;
|
||||
|
||||
typedef struct {
|
||||
TType type;
|
||||
double num; /* - hodnota */
|
||||
char op; /* - + - * / ^ ~ */
|
||||
char func[8]; /* - sin cos sqrt log ln */
|
||||
} Token;
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - jednoduche pole tokenov */
|
||||
/* ---------------------------- */
|
||||
typedef struct {
|
||||
Token *a;
|
||||
int n;
|
||||
int cap;
|
||||
} TVec;
|
||||
|
||||
/* - init */
|
||||
static void tvec_init(TVec *v) { v->a=NULL; v->n=0; v->cap=0; }
|
||||
|
||||
/* - free */
|
||||
static void tvec_free(TVec *v) { free(v->a); tvec_init(v); }
|
||||
|
||||
/* - push */
|
||||
static int tvec_push(TVec *v, Token t) {
|
||||
if (v->n >= v->cap) {
|
||||
int nc = v->cap ? v->cap*2 : 32;
|
||||
Token *p = realloc(v->a, nc*sizeof(Token));
|
||||
if (!p) return 0;
|
||||
v->a = p;
|
||||
v->cap = nc;
|
||||
}
|
||||
v->a[v->n++] = t;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - priorita operatorov */
|
||||
/* ---------------------------- */
|
||||
static int prec(char op) {
|
||||
if (op=='~') return 4; /* - unarne minus */
|
||||
if (op=='^') return 3; /* - mocnina */
|
||||
if (op=='*' || op=='/') return 2; /* - nasob/del */
|
||||
if (op=='+' || op=='-') return 1; /* - plus/minus */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* - prava asociativita */
|
||||
static int right_assoc(char op) {
|
||||
return (op=='^' || op=='~'); /* - pow, unary */
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - tokenize: string -> tokeny */
|
||||
/* ---------------------------- */
|
||||
static int tokenize(const char *s, TVec *out) {
|
||||
/* - reset error */
|
||||
set_err("OK");
|
||||
|
||||
tvec_init(out);
|
||||
|
||||
/* - prev token */
|
||||
TType prev = T_OP; /* - na zaciatku */
|
||||
|
||||
while (*s) {
|
||||
/* - skip spaces */
|
||||
while (isspace((unsigned char)*s)) s++;
|
||||
|
||||
if (!*s) break;
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - cislo */
|
||||
/* ---------------------------- */
|
||||
if (isdigit((unsigned char)*s) || *s=='.') {
|
||||
char *end = NULL;
|
||||
double x = strtod(s, &end);
|
||||
if (end==s) { set_err("Zle cislo"); return 0; }
|
||||
|
||||
Token t = {0};
|
||||
t.type = T_NUM;
|
||||
t.num = x;
|
||||
|
||||
if (!tvec_push(out, t)) { set_err("No memory"); return 0; }
|
||||
|
||||
s = end;
|
||||
prev = T_NUM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - ident: funkcia/konstanta */
|
||||
/* ---------------------------- */
|
||||
if (isalpha((unsigned char)*s)) {
|
||||
char buf[16]={0};
|
||||
int i=0;
|
||||
|
||||
while (isalpha((unsigned char)*s) && i<15) {
|
||||
buf[i++] = (char)tolower((unsigned char)*s);
|
||||
s++;
|
||||
}
|
||||
buf[i]=0;
|
||||
|
||||
/* - konstanta pi */
|
||||
if (!strcmp(buf,"pi")) {
|
||||
Token t={0};
|
||||
t.type=T_NUM;
|
||||
t.num=M_PI;
|
||||
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
||||
prev=T_NUM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* - konstanta e */
|
||||
if (!strcmp(buf,"e")) {
|
||||
Token t={0};
|
||||
t.type=T_NUM;
|
||||
t.num=M_E;
|
||||
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
||||
prev=T_NUM;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* - funkcie */
|
||||
if (!strcmp(buf,"sin") || !strcmp(buf,"cos") ||
|
||||
!strcmp(buf,"sqrt") || !strcmp(buf,"log") ||
|
||||
!strcmp(buf,"ln")) {
|
||||
|
||||
Token t={0};
|
||||
t.type=T_FUNC;
|
||||
strncpy(t.func, buf, sizeof(t.func)-1);
|
||||
|
||||
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
||||
prev=T_FUNC;
|
||||
continue;
|
||||
}
|
||||
|
||||
set_err("Neznama funkcia");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - zatvorky */
|
||||
/* ---------------------------- */
|
||||
if (*s=='(') {
|
||||
Token t={0};
|
||||
t.type=T_LP;
|
||||
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
||||
s++;
|
||||
prev=T_LP;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*s==')') {
|
||||
Token t={0};
|
||||
t.type=T_RP;
|
||||
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
||||
s++;
|
||||
prev=T_RP;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - operatory */
|
||||
/* ---------------------------- */
|
||||
if (strchr("+-*/^", *s)) {
|
||||
char op = *s;
|
||||
|
||||
/* - unary minus */
|
||||
if (op=='-' && !(prev==T_NUM || prev==T_RP)) {
|
||||
op='~';
|
||||
}
|
||||
|
||||
Token t={0};
|
||||
t.type=T_OP;
|
||||
t.op=op;
|
||||
|
||||
if (!tvec_push(out,t)) { set_err("No memory"); return 0; }
|
||||
|
||||
s++;
|
||||
prev=T_OP;
|
||||
continue;
|
||||
}
|
||||
|
||||
set_err("Neplatny znak");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (out->n==0) { set_err("Prazdny vyraz"); return 0; }
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - infix -> postfix */
|
||||
/* ---------------------------- */
|
||||
static int to_postfix(const TVec *in, TVec *out) {
|
||||
tvec_init(out);
|
||||
|
||||
TVec st; /* - operator stack */
|
||||
tvec_init(&st);
|
||||
|
||||
for (int i=0;i<in->n;i++) {
|
||||
Token t = in->a[i];
|
||||
|
||||
/* - cislo -> output */
|
||||
if (t.type==T_NUM) {
|
||||
if (!tvec_push(out,t)) { set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
/* - funkcia -> stack */
|
||||
else if (t.type==T_FUNC) {
|
||||
if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
/* - operator */
|
||||
else if (t.type==T_OP) {
|
||||
while (st.n>0) {
|
||||
Token top = st.a[st.n-1];
|
||||
|
||||
/* - funkcia ma prioritu */
|
||||
if (top.type==T_FUNC) {
|
||||
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
||||
st.n--;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* - operator priorita */
|
||||
if (top.type==T_OP) {
|
||||
int p1 = prec(t.op);
|
||||
int p2 = prec(top.op);
|
||||
|
||||
/* - pop pri vacsej priorite */
|
||||
if (p2>p1 || (p2==p1 && !right_assoc(t.op))) {
|
||||
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
||||
st.n--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
/* - ( -> stack */
|
||||
else if (t.type==T_LP) {
|
||||
if (!tvec_push(&st,t)) { set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
/* - ) -> pop do ( */
|
||||
else if (t.type==T_RP) {
|
||||
int ok=0;
|
||||
|
||||
while (st.n>0) {
|
||||
Token top = st.a[--st.n];
|
||||
|
||||
if (top.type==T_LP) { ok=1; break; }
|
||||
|
||||
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
if (!ok) { set_err("Zle zatvorky"); goto fail; }
|
||||
|
||||
/* - po zatvorke funkcia */
|
||||
if (st.n>0 && st.a[st.n-1].type==T_FUNC) {
|
||||
Token f = st.a[--st.n];
|
||||
if (!tvec_push(out,f)) { set_err("No memory"); goto fail; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* - pop zvysok */
|
||||
while (st.n>0) {
|
||||
Token top = st.a[--st.n];
|
||||
if (top.type==T_LP) { set_err("Zle zatvorky"); goto fail; }
|
||||
if (!tvec_push(out,top)) { set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
tvec_free(&st);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
tvec_free(&st);
|
||||
tvec_free(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - stack double */
|
||||
/* ---------------------------- */
|
||||
typedef struct {
|
||||
double *a;
|
||||
int n;
|
||||
int cap;
|
||||
} DStack;
|
||||
|
||||
static void ds_init(DStack *s){ s->a=NULL; s->n=0; s->cap=0; }
|
||||
static void ds_free(DStack *s){ free(s->a); ds_init(s); }
|
||||
|
||||
static int ds_push(DStack *s, double x){
|
||||
if (s->n>=s->cap){
|
||||
int nc=s->cap? s->cap*2:32;
|
||||
double *p=realloc(s->a,nc*sizeof(double));
|
||||
if(!p) return 0;
|
||||
s->a=p; s->cap=nc;
|
||||
}
|
||||
s->a[s->n++]=x;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ds_pop(DStack *s, double *x){
|
||||
if(s->n==0) return 0;
|
||||
*x=s->a[--s->n];
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - apply function */
|
||||
/* ---------------------------- */
|
||||
static int apply_func(const char *f, double x, double *out){
|
||||
if(!strcmp(f,"sin")) { *out=sin(x); return 1; }
|
||||
if(!strcmp(f,"cos")) { *out=cos(x); return 1; }
|
||||
|
||||
if(!strcmp(f,"sqrt")){
|
||||
if(x<0) { set_err("sqrt domena"); return 0; }
|
||||
*out=sqrt(x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!strcmp(f,"log")){
|
||||
if(x<=0) { set_err("log domena"); return 0; }
|
||||
*out=log10(x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(!strcmp(f,"ln")){
|
||||
if(x<=0) { set_err("ln domena"); return 0; }
|
||||
*out=log(x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
set_err("Zla funkcia");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - eval postfix */
|
||||
/* ---------------------------- */
|
||||
static int eval_postfix(const TVec *post, double *out){
|
||||
DStack st;
|
||||
ds_init(&st);
|
||||
|
||||
for(int i=0;i<post->n;i++){
|
||||
Token t=post->a[i];
|
||||
|
||||
/* - cislo -> push */
|
||||
if(t.type==T_NUM){
|
||||
if(!ds_push(&st,t.num)){ set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
/* - funkcia */
|
||||
else if(t.type==T_FUNC){
|
||||
double a,r;
|
||||
if(!ds_pop(&st,&a)){ set_err("Syntax"); goto fail; }
|
||||
if(!apply_func(t.func,a,&r)) goto fail;
|
||||
if(!ds_push(&st,r)){ set_err("No memory"); goto fail; }
|
||||
}
|
||||
|
||||
/* - operator */
|
||||
else if(t.type==T_OP){
|
||||
|
||||
/* - unary minus */
|
||||
if(t.op=='~'){
|
||||
double a;
|
||||
if(!ds_pop(&st,&a)){ set_err("Syntax"); goto fail; }
|
||||
if(!ds_push(&st,-a)){ set_err("No memory"); goto fail; }
|
||||
continue;
|
||||
}
|
||||
|
||||
/* - binarne */
|
||||
double b,a,r;
|
||||
if(!ds_pop(&st,&b) || !ds_pop(&st,&a)){ set_err("Syntax"); goto fail; }
|
||||
|
||||
if(t.op=='+') r=a+b;
|
||||
else if(t.op=='-') r=a-b;
|
||||
else if(t.op=='*') r=a*b;
|
||||
else if(t.op=='/'){
|
||||
if(b==0){ set_err("Delenie nulou"); goto fail; }
|
||||
r=a/b;
|
||||
}
|
||||
else if(t.op=='^'){
|
||||
r=pow(a,b);
|
||||
if(isnan(r) || isinf(r)){ set_err("Pow domena"); goto fail; }
|
||||
}
|
||||
else { set_err("Zly operator"); goto fail; }
|
||||
|
||||
if(!ds_push(&st,r)){ set_err("No memory"); goto fail; }
|
||||
}
|
||||
else{
|
||||
set_err("Syntax");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* - musi ostat 1 */
|
||||
if(st.n!=1){ set_err("Syntax"); goto fail; }
|
||||
|
||||
*out=st.a[0];
|
||||
ds_free(&st);
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
ds_free(&st);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------- */
|
||||
/* - hlavna funkcia */
|
||||
/* ---------------------------- */
|
||||
calc_status_t calc_evaluate(const char *expr, double *out){
|
||||
if(!expr || !out){ set_err("NULL"); return CALC_ERR; }
|
||||
|
||||
TVec toks, post;
|
||||
tvec_init(&toks);
|
||||
tvec_init(&post);
|
||||
|
||||
/* - tokenize */
|
||||
if(!tokenize(expr,&toks)){ tvec_free(&toks); return CALC_ERR; }
|
||||
|
||||
/* - postfix */
|
||||
if(!to_postfix(&toks,&post)){ tvec_free(&toks); return CALC_ERR; }
|
||||
|
||||
/* - eval */
|
||||
if(!eval_postfix(&post,out)){
|
||||
tvec_free(&toks);
|
||||
tvec_free(&post);
|
||||
return CALC_ERR;
|
||||
}
|
||||
|
||||
tvec_free(&toks);
|
||||
tvec_free(&post);
|
||||
return CALC_OK;
|
||||
}
|
||||
|
||||
18
sk2/calculator.h
Normal file
18
sk2/calculator.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef CALCULATOR_H
|
||||
#define CALCULATOR_H
|
||||
|
||||
typedef enum {
|
||||
CALC_OK = 0,
|
||||
CALC_ERR
|
||||
} calc_status_t;
|
||||
|
||||
/* - vyhodnoti vyraz */
|
||||
/* - expr: vstup */
|
||||
/* - out: vysledok */
|
||||
calc_status_t calc_evaluate(const char *expr, double *out);
|
||||
|
||||
/* - text chyby */
|
||||
const char *calc_error(void);
|
||||
|
||||
#endif
|
||||
|
||||
35
sk2/main.c
Normal file
35
sk2/main.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include "calculator.h"
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* - pekny vypis */
|
||||
static void print_double(double x){
|
||||
/* - cele cislo */
|
||||
if (fabs(x - round(x)) < 1e-9) {
|
||||
printf("%.0f\n", round(x));
|
||||
return;
|
||||
}
|
||||
/* - min 2 des */
|
||||
printf("%.2f\n", x);
|
||||
}
|
||||
|
||||
int main(void){
|
||||
char line[512];
|
||||
|
||||
/* - citaj riadky */
|
||||
while (fgets(line,sizeof(line),stdin)){
|
||||
double res;
|
||||
|
||||
/* - vypocitaj */
|
||||
if (calc_evaluate(line,&res) != CALC_OK){
|
||||
fprintf(stderr,"Chyba: %s\n", calc_error());
|
||||
continue;
|
||||
}
|
||||
|
||||
/* - vypis */
|
||||
print_double(res);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user