《利用棧實現含浮點數的算術表達式的計算》

 

1、基本思路:

先將算術表達式(中綴式)讀入到string對象中,再將其經過相關操作解析並轉換成後綴表達式(存放在一條鏈表中),最後才(遍歷該鏈表)對該表達式進行運算。


2、如何支持浮點數的運算:
a、使用atof()函數,它是C語言標準庫中的一個字符串處理函數,功能是把字符串轉換成浮點數,其頭文件爲<stdlib.h>,該函數名是 "ASCII to floating point numbers" 的縮寫。
語法格式爲:double atof(const char *nptr)。
b、說明:atof()會掃描參數nptr字符串,跳過前面的空格字符,直到遇上數字或正負符號纔開始做轉換,而再遇到非數字或字符串結束時('\0')才結束轉換,並將結果返回。

 

3、具體實現代碼(含詳細註釋,不懂的地方可以留言):

 

 

#include<iostream>
#include<stack>
#include<stdlib.h>
#include<string>
using namespace std;

//使用結點Node類來存儲操作數或運算符
struct Node
{
	bool isOperand; //判斷結點裏存儲的是否爲操作數
	double operand;
	char Operator;
	Node* next;
	Node(bool isOpd = false, double opd = 0.0, char Opr = ' ')
	{
		isOperand = isOpd;
		operand = opd;
		Operator = Opr;
		next = nullptr;
	}
};

//定義一個Calculator類來存放計算後綴式算術表達式時所需的基本操作
class Calculator
{
public:
	Calculator() {}
	double runPostfixExpression(Node* head); //執行對後綴式算術表達式的計算

private:
	stack<double> st;  //存放操作數(double類型)的棧
	void addOperand(double value);  //操作數進棧
	bool get2Operands(double& left, double& right); //從棧中取出(退出)兩個操作數
	bool doOperator(char op); //使用該運算符進行一次基本算術運算
};

double Calculator::runPostfixExpression(Node* head)
{
	double newOperand;
	while (head != nullptr)
	{
		if (head->isOperand) addOperand(head->operand); //是操作數,直接進棧
		else doOperator(head->Operator);  //是運算符,執行基本算術運算
		head = head->next;
	}
	newOperand = st.top();
	return newOperand;
}

void Calculator::addOperand(double Operand)
{
	st.push(Operand);
}

bool Calculator::get2Operands(double& leftOperand, double& rightOperand)
{
	if (st.empty())
	{
		cerr << "缺少右操作數!" << endl;
		return false;
	}
	rightOperand = st.top();
	st.pop();
	if (st.empty())
	{
		cerr << "缺少左操作數!" << endl;
		return false;
	}
	leftOperand = st.top();
	st.pop();
	return true;
}

bool Calculator::doOperator(char op)
{
	double left, right, value;
	bool result = get2Operands(left, right);
	if (result)
	{
		switch (op)
		{
		case '+':
			value = left + right;
			st.push(value);
			break;
		case '-':
			value = left - right;
			st.push(value);
			break;
		case '*':
			value = left * right;
			st.push(value);
			break;
		case '/':
			if (right == 0.0)
			{
				cerr << "Divided by 0!" << endl;
				return false;
			}
			else
			{
				value = left / right;
				st.push(value);
			}
			break;
		}
		return true;
	}
	else return false;
}

//isp(in stack priority)爲該算術運算符棧內優先級
int isp(char c)
{
	switch (c)
	{
	case '#': return 0;
	case '(': return 1;
	case '*':case'/': return 5;
	case '+':case '-': return 3;
	case ')': return 6;
	default: return -1;
	}
}

//icp(in coming priority)爲該算術運算符棧外優先級
int icp(char c)
{
	switch (c)
	{
	case '#': return 0;
	case '(': return 6;
	case '*':case'/': return 4;
	case '+':case '-': return 2;
	case ')': return 1;
	default: return -1;
	}
}

Node* infixToPostfix(string str)
{
	double operand;
	//給中綴算術表達式str末尾添加個字符'#',以便利於後面棧內外運算符優先級比較時出棧的處理
	str = str + '#';
	Node* head, *current;
	stack <char> st;
	char charInStack; //存放在棧st中的操作數或運算符
	int i = 0;
	while (i < str.size())
	{
		if (isdigit(str[i])) //如果是操作數,則處理(取浮點數)後便可直接存入Node對象中
		{
			if (i == 0)  //處理中綴表達式str中開頭的操作數
			{
				//讀取該操作數(double類型)並存入Node對象中
				operand = atof(&str[i]);
				current = new Node(true, operand, 0.0);
				head = current; //建立表頭結點
			}
			else  //處理中綴表達式str中的其他操作數
			{
				//讀取該操作數(double類型)並存入Node對象中
				operand = atof(&str[i]);
				current->next = new Node(true, operand, 0.0);
				current = current->next;
			}
			while (isdigit(str[i]) || str[i] == '.') //過濾掉(跳過)string轉換double時已被使用的那些char字符
			{
				i++;
			}
		}
		else  //如果是運算符,則進行一系列(棧內外)運算符優先級的比較並結合相關的入棧出棧操作,適時將運算符存入Node對象
		{
			if (st.empty())
			{
				if (i == str.size() - 1) break;  //若此時棧空且str已被掃描完,則結束循環
				else //若此時棧空且str還未被掃描完,則繼續將當前掃描到的運算符進棧
				{
					st.push(str[i]);
					i++;
				}
			}
			else
			{
				charInStack = st.top();
				//棧內運算符優先級<棧外運算符優先級,則將棧外運算符str[i]直接進棧
				if (isp(charInStack) < icp(str[i]))
				{
					st.push(str[i]);
					i++;
				}
				//棧內運算符優先級>棧外運算符優先級,則將棧頂的運算符出棧並將其存入Node對象中
				else if (isp(charInStack) > icp(str[i]))
				{
					charInStack = st.top();
					st.pop();
					current->next = new Node(false, 0.0, charInStack);
					current = current->next;
				}
				//棧內運算符優先級=棧外運算符優先級,此時只有一種可能,即棧內的'('/')'的優先級與棧外的')'/'('優先級相等且均爲1/6,
				//即匹配情況爲“()”或“)(”,此時直接將棧內的運算符出棧,丟棄當前所掃描的str[i] 運算符,繼續掃描下一個運算符
				else
				{
					st.pop();
					i++;
				}
			}
		}
	}
	return head;
}

int main(void)
{
	string expression;
	while (1)
	{
		Calculator c;
		cout << "請輸入算術表達式:" << endl;
		cin >> expression;
		cout << "Result = " << c.runPostfixExpression(infixToPostfix(expression)) << endl;
	}
	return 0;
}

 

順便附上項目源碼,支持開源精神,歡迎star、fork:
https://github.com/Yuziquan/CalculateArithmeticExpressionForFloatingpoint

(希望可以幫到有需要的人~~)

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章