ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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

     

    사칙연산

    111+1×2를 계산하면, 곱셈을 덧셈보다 먼저 하니까 113이 정답이다. 그런데 이걸 왼쪽부터 차례대로 계산해서 224가 답이라고 우기는 사람도 있다. 그러고 보니 왜 덧셈보다 곱셈을 먼저 하도록 규

    terms.naver.com

     

    코드(header) 문서

    TEXTfile.txt
    0.01MB


    실행결과

    수식을 문자열로 입력 -> 계산결과 출력


    함수

    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 프로그램 소개를 마칩니다.

하면된다 學業報國