Program_Light

KMS05 : C언어 분수 계산기 MSNCCL fraction (KMS number calculator)

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