KMS05 : C언어 분수 계산기 MSNCCL fraction (KMS number calculator)
: 이 프로그램은 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
사칙연산
111+1×2를 계산하면, 곱셈을 덧셈보다 먼저 하니까 113이 정답이다. 그런데 이걸 왼쪽부터 차례대로 계산해서 224가 답이라고 우기는 사람도 있다. 그러고 보니 왜 덧셈보다 곱셈을 먼저 하도록 규
terms.naver.com
코드(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 프로그램 소개를 마칩니다.