-
KMS05 : C언어 분수 계산기 MSNCCL fraction (KMS number calculator)Program_Light 2021. 1. 23. 13:00
: 이 프로그램은 2019년 1월 8일 23:07분에 작성된 naver 블로그의 글을 기본으로 합니다.
안녕하세요 tomskang입니다. 오늘은 지난법 C++계산기 std를 개조해서 C++ 분수계산기를 만들었습니다. 그냥 계산기와 차이점은 계산결과가 분수로 표시된다는 것입니다. 예를 들어 일반 계산기에 "2/3*4"라고 입력하면 2.6666666666...이라고 뜨지만, 분수 계산기에서는 8/3이라고 뜹니다. 나누기 계산에 있어서 상당히 보기가 편합니다.
분수 계산기는 기본적인 사칙연산에 제곱을 추가해 5가지의 연산자를 계산할 수 있습니다.
소스코드 보여드리도록 하겠습니다.
소스코드
MSNCCLFRCT.h (504 line)
/* **************************************** MS New CalCuLator project MSNCCL standard made by mskang made to 2018/12/22 - 2018/12/23 **************************************** */ #ifndef __MS_NCCL_H__ #define __MS_NCCL_H__ #include <cstdio> #include <cctype> #include <cmath> #include <cstring> #include <stack> #include <queue> #include <algorithm> #include <cstdarg> using namespace std; #define TRUE 1 #define FALSE 0 #define TWOOP "+-*/^" #define ONEOP "!AB" //A[sqrt] B[cbrt] /* ********************** struct & union ********************** */ long long GetGCD(long double A, long double B) { A = fabsl(A); B = fabsl(B); while (A != B) { if (A > B) A -= B; else B -= A; } return (long long)A; } long long GetLCM(long double A, long double B) { return (long long)(A * B / GetGCD(A, B)); } long long GetLIS(long double A, long double B) { long long GCD; GCD = GetGCD(A, B); return (long long)(A * B / GCD); } enum { NUM, OP }; typedef union _X01 { long double number; char op; }_value; typedef struct _X02 { short type; _value value; }_factor; typedef struct _X03 { long double denominator; long double numerator; void RAF(void) { // reduce a fraction long long GCD; if (floorl(numerator) != numerator) { do { denominator *= 10; numerator *= 10; } while (floorl(numerator) == numerator); } if (floorl(denominator) != denominator) { // must not change to else if do { denominator *= 10; numerator *= 10; } while (floorl(denominator) == denominator); } GCD = GetGCD(denominator, numerator); denominator /= GCD; numerator /= GCD; return; } }fraction; typedef queue< _factor > EXP; /* ********************** Func for use ********************** */ void PrintEXP(EXP exp); EXP Parsing(const char * string); EXP GetPostfix(EXP exp); fraction CalcResult(EXP PFexp); fraction MSNCCLFRCT(const char * string); /* ********************** Printing ********************** */ void PrintEXP(EXP exp) { _factor factor; printf("\n ======================== Print EXP ======================== \n"); while (!exp.empty()) { factor = exp.front(); exp.pop(); printf(" type : %s ", (factor.type == NUM) ? "Num" : "Op "); if (factor.type == NUM) printf("value : %lf", factor.value.number); else printf("value : %c", factor.value.op); printf("\n"); } printf(" ======================== Print EXP ======================== \n"); return; } /* ********************** Parsing ********************** */ void _inputnum(bool * dot, bool * num, bool * minus, long double * number, int * underdot, EXP * pexp) { _factor factor; if (*dot) *number /= powl(10, *underdot); if (*minus) *number *= -1; factor.type = NUM; factor.value.number = *number; pexp->push(factor); *number = *underdot = 0; *dot = *num = *minus = false; return; } EXP Parsing(const char * string) { EXP exp; _factor factor; long double number = 0; int underdot = 0; bool dot, num, minus; int i; char tok; dot = num = minus = false; for (i = 0; string[i] != NULL; i++) { if (string[i] == ' ') continue; // make number // tok = string[i]; if (isdigit(tok)) { num = true; number *= 10; number += tok - '0'; if (dot) underdot++; } else if (tok == '.') { dot = true; underdot = 0; } else if ((tok == '-') && (string[i - 1] != ')') && (!isdigit(string[i - 1]))) { minus = true; } // input number & op // else { if (num) { _inputnum(&dot, &num, &minus, &number, &underdot, &exp); } factor.type = OP; if ((tok == '{') || (tok == '[')) tok = '('; else if ((tok == '}') || (tok == ']')) tok = ')'; else if (!strncmp(string + i, "sqrt", 4)) { tok = 'A'; i += 3; } else if (!strncmp(string + i, "cbrt", 4)) { tok = 'B'; i += 3; } factor.value.op = tok; exp.push(factor); } } if (num) { _inputnum(&dot, &num, &minus, &number, &underdot, &exp); } return exp; } /* ********************** Post fix ********************** */ int _GetOpPrec(const char op) { // set operator precedence // switch (op) { case 'A': case 'B': return 9; case '^': case '!': return 7; case '*': case '/': return 5; case '+': case '-': return 3; case '(': return 1; default: return -1; } } int _IsOpPrec(const char Op1, const char Op2) { int Prec1, Prec2; Prec1 = _GetOpPrec(Op1); Prec2 = _GetOpPrec(Op2); if (Prec1 > Prec2) return 1; else if (Prec1 == Prec2) return 0; else return -1; } EXP GetPostfix(EXP exp) { EXP pfexp; // PostFixEXP stack <char> Opstack; _factor factor, input; input.type = OP; while (!exp.empty()) { factor = exp.front(); exp.pop(); if (factor.type == NUM) pfexp.push(factor); else { switch (factor.value.op) { case '(': Opstack.push(factor.value.op); break; case ')': while (!Opstack.empty() && Opstack.top() != '(') { input.value.op = Opstack.top(); Opstack.pop(); pfexp.push(input); } Opstack.pop(); break; default: while (!Opstack.empty() && _IsOpPrec(Opstack.top(), factor.value.op) >= 0) { input.value.op = Opstack.top(); pfexp.push(input); Opstack.pop(); } Opstack.push(factor.value.op); } } } while (!Opstack.empty()) { input.value.op = Opstack.top(); Opstack.pop(); pfexp.push(input); } return pfexp; } /* ********************** Calculating ********************** */ fraction PlusCalc(fraction N1, fraction N2) { fraction result; long long LCM; LCM = GetLCM(N1.denominator, N2.denominator); N1.numerator *= (LCM / N1.denominator); N2.numerator *= (LCM / N2.denominator); result.numerator = N1.numerator + N2.numerator; result.denominator = LCM; result.RAF(); return result; } fraction MinusCalc(fraction N1, fraction N2) { fraction result; long long LCM; LCM = GetLCM(N1.denominator, N2.denominator); N1.numerator *= (LCM / N1.denominator); N2.numerator *= (LCM / N2.denominator); result.numerator = N1.numerator - N2.numerator; result.denominator = (long double)LCM; result.RAF(); return result; } fraction MuntipleCalc(fraction N1, fraction N2) { fraction result; result.denominator = N1.denominator * N2.denominator; result.numerator = N1.numerator * N2.numerator; result.RAF(); return result; } fraction DivideCalc(fraction N1, fraction N2) { fraction result; result.denominator = N1.denominator * N2.numerator; result.numerator = N1.numerator * N2.denominator; result.RAF(); return result; } fraction PowCalc(fraction N1, fraction N2) { fraction result; long double pownum; pownum = N2.numerator / N2.denominator; result.denominator = powl(N1.denominator, pownum); result.numerator = powl(N1.numerator, pownum); result.RAF(); return result; } fraction FactorialCalc(fraction N1) { fraction result{1, 1}; long double facnum; int i; facnum = N1.numerator / N1.denominator; for (i = 1; i <= facnum; i++) { result.numerator *= i; } return result; } fraction SqrtCalc(fraction N1) { fraction result{ 1, 1 }; result.numerator = sqrt(N1.numerator); result.denominator = sqrt(N1.denominator); return result; } fraction CbrtCalc(fraction N1) { fraction result{ 1, 1 }; result.numerator = cbrt(N1.numerator); result.denominator = cbrt(N1.denominator); return result; } fraction Calc(char Op, int n, ...) { // set how to operating // va_list nums; fraction N1, N2; fraction result = { 0, 0 }; if (strchr(TWOOP, Op)) { va_start(nums, n); N1 = va_arg(nums, fraction); N2 = va_arg(nums, fraction); va_end(nums); switch (Op) { case'+': result = PlusCalc(N1, N2); break; case'-': result = MinusCalc(N1, N2); break; case'*': result = MuntipleCalc(N1, N2); break; case'/': result = DivideCalc(N1, N2); break; case '^': result = PowCalc(N1, N2); break; default: result = PlusCalc(N1, N2); break; } } else if (strchr(ONEOP, Op)) { n = 1; va_start(nums, n); N1 = va_arg(nums, fraction); va_end(nums); switch (Op) { case '!': result = FactorialCalc(N1); break; case 'A': result = SqrtCalc(N1); break; case 'B': result = CbrtCalc(N1); break; default: return N1; break; } } else return {-1, 1}; return result; } fraction CalcResult(EXP PFexp) { fraction result; stack <fraction> Calcstack; _factor factor; fraction fracfac; fraction P1, P2, C; // pop1, pop2, calc while (!PFexp.empty()) { factor = PFexp.front(); PFexp.pop(); if (factor.type == NUM) { fracfac.numerator = factor.value.number; fracfac.denominator = 1; Calcstack.push(fracfac); } else { if (strchr(TWOOP, factor.value.op)) { P2 = Calcstack.top(); Calcstack.pop(); P1 = Calcstack.top(); Calcstack.pop(); C = Calc(factor.value.op, 2, P1, P2); Calcstack.push(C); } else if (strchr(ONEOP, factor.value.op)) { P1 = Calcstack.top(); Calcstack.pop(); C = Calc(factor.value.op, 1, P1); Calcstack.push(C); } } } result = Calcstack.top(); return result; } /* ********************** MSNCCL ********************** */ fraction MSNCCLFRCT(const char * string) { EXP exp; fraction result; exp = Parsing(string); //PrintEXP(exp); exp = GetPostfix(exp); //PrintEXP(exp); result = CalcResult(exp); if (result.denominator < 0) { result.denominator = fabsl(result.denominator); result.numerator *= -1; } return result; } #endif
source.cpp
#include "MSNCCLFRCT.h" int main(void) { EXP Texp; EXP pfexp; fraction fr; char * string = (char *)malloc(sizeof(char) * 100); printf("EXP : "); scanf("%s", string); fr = MSNCCL(string); printf("%lf / %lf \n", fr.numerator, fr.denominator); printf("Ms New Calculator Standard / made by mskang \n "); return 0; }
입력 : 수식을 띄어쓰기 없이 입력
출력 : 사칙연산 순서대로 수식을 연산해 결과출력
참고자료
* 사칙연산 순서
https://terms.naver.com/entry.nhn?docId=3567720&cid=58944&categoryId=58970
코드(header) 문서
실행결과
함수
Parsing, Postfix, Calculating, MSNCCL, Print
*소스코드가 너무 길어서 일일이 설명해드릴 순 없으니, 간단하게 중요 함수 설명만 하고 넘어가도록 하겠습니다.
Parsing
EXP Parsing(const char * string)
문자열 하나가 들어오면 그 문자열을 컴퓨터가 인식할 수 있는 형태로 수식을 바꿉니다. EXP는 제가 만든 컴퓨터가 인식할 수 있도록 수식을 저장하는 큐입니다.
Postfix
EXP GetPostfix(EXP exp);
우리가 아는 수식에서 후위 표기법으로 수식을 변환해줍니다. 후위 표기법으로 수식을 변환해 컴퓨터가 수식을 계산하기 쉽게 만듭니다.
Calculating
fraction CalcResult(EXP PFexp);
후위 표기법으로 변환된 수식을 받아 그 수식을 계산해 계산 결과를 반환해줍니다.
여기서 계산결과의 형태인 fraction은 long double로 이루어져 있는 분모와 분자를 저장하는 구조체 입니다.
MSNCCL
fraction MSNCCL(const char * string) { EXP exp; fraction result; exp = Parsing(string); exp = GetPostfix(exp); result = CalcResult(exp); if (result.denominator < 0) { result.denominator = fabsl(result.denominator); result.numerator *= -1; } return result; }
앞에서 소개한 세 가지 함수를 일렬로 배열해 문자열을 계산해 결과는 출력합니다.
이상으로 tomskang의 MSNCCLFRCT 프로그램 소개를 마칩니다.
'Program_Light' 카테고리의 다른 글
KMS01 : 자작 Algorithm Tayo Algorithm (0) 2021.03.27 KMS04 : C++ 소수탐색 프로그램 Ver3 (source) (0) 2021.03.20 KMS04 : C++ 소수탐색 프로그램 Ver2 (source) (0) 2021.03.06 KMS04 : C언어 소수탐색 프로그램 Ver1 (source) (0) 2021.02.27 (구)KMS06 : C언어 소인수분해 프로그램 + source (MSFIP) (0) 2021.02.20