VC6.0计算器(逆波兰) 短学期作业
来源:互联网 发布:nano接收器配对软件 编辑:程序博客网 时间:2024/05/16 18:47
学校短学期的作业,最初没有头绪的时候参考了 新一凡的博客
http://blog.csdn.net/u013240179/article/details/39105843
上对变量的一些设置以及部分算法,对此表示十分感谢。
同时我在此基础上做出了许多改进。
使用的是Visual C++ 6.0
采用的是逆波兰算法
实现了:
1含有括号的加减乘除运算;
2sin、cos、tan、开方、幂函数等运算;
3十进制数转二进制数(整数和小数);
4二进制转十进制(整数)(由于浮点数计算问题小数转换未能实现);
首先,是创建工程文件
选择 MFC AppWizard(exe),输入工程名称,点击创建,
选择单文档(single document),然后一直点下一步,如图所示
工程创建好后,再添加类,再次就不一一赘述。
绑定的变量在此初始化为空
/////////////////////////////////////////////////////////////////////////////// CMycalculatorDlg dialogCMycalculatorDlg::CMycalculatorDlg(CWnd* pParent /*=NULL*/) : CDialog(CMycalculatorDlg::IDD, pParent){ //{{AFX_DATA_INIT(CMycalculatorDlg) m_sOutString = _T(""); //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_sOutString = "";//初始化为空}
给添加的每个按键的按钮绑定的函数添加的代码如下
void CMycalculatorDlg::OnPress1() {// TODO: Add your control notification handler code herem_sOutString=m_sOutString+"1";UpdateData(false);//将数据显示在对话框中}
下面,谈一谈整个工程的核心部分,逆波兰表达式的转换与计算
//Exchange函数**************************************//将前缀式转化成逆波兰表达式void CCounter::Exchange(){ m_sOperator.push_back ("#");//运算符堆栈最底端为# int i = 0;//用于计数用的临时变量,默认为0 for(i=0;i<m_string.GetLength();i++) { //若为sin cos tan square等单元运算符********** if(m_string.GetAt(i)>='a'&&m_string.GetAt(i)<='z') { CString str;//用于存储单元运算符名称的字符串 while(i<m_string.GetLength()&& m_string.GetAt(i)>='a' && m_string.GetAt(i)<='z' )//当 { str+=m_string.GetAt(i) ; i++; }//至此,str即为单元运算符名称 CalculateOne(i,str); i=loop; continue; } //若为左括号,将括号压入运算符堆栈 if( m_string.GetAt(i)=='(' ) { m_sOperator.push_back(m_string.GetAt(i)); continue; } //若为右括号,则将左右括号之间的运算符依次出栈,存入操作数堆栈 if( m_string.GetAt(i)==')' ) { while(m_sOperator.back()!='(') { m_sOprand.push_back(m_sOperator.back());//将运算符存入操作数栈 m_sOperator.pop_back ();//从运算符栈弹出运算符 } m_sOperator.pop_back ();//弹出左括号 continue; } //如果为二元运算符,不是左右括号 if( IsOprand( m_string.GetAt(i) )) { //若后进运算符优先级高于栈顶元素,则继续进栈 if(IsPriority (m_string.GetAt(i), m_sOperator.back())) { m_sOperator.push_back(m_string.GetAt(i)); } else //若后进运算符优先级低于或等于栈顶元素, { m_sOprand.push_back( m_sOperator.back() );//栈顶元素存入逆波兰数组 m_sOperator.pop_back ();//栈顶元素出栈 m_sOperator.push_back(m_string.GetAt(i));//后进运算符存入运算符堆栈 }//end else continue; }//end if( 如果为运算符,不是左右括号) //若为数字 if(m_string.GetAt(i)>='0'&& m_string.GetAt(i)<='9') { CString num="";//t为临时变量,将运算符之间的数字和小数点存为一整个字符串t while(i<m_string.GetLength()&& (m_string.GetAt(i)>='0' && m_string.GetAt(i)<='9'||m_string.GetAt(i)=='.') ) { num+=m_string.GetAt(i) ; i++; } if(i<m_string.GetLength()) { i--; }//由于在for中有i++,故先i-- m_sOprand.push_back(num);//将由数字和小数点组成的字符串num整个存入操作数栈 }//end }//end for //将运算符堆栈里的运算符压入逆波兰数组(操作数栈) while(m_sOperator.back()!='#') { m_sOprand.push_back(m_sOperator.back()); //把运算符栈栈顶元素压入操作符栈 m_sOperator.pop_back();//弹出运算符栈栈顶元素 }//此时m_sOperator中只有#,m_sOprand即为逆波兰数组}//end function exchange//函数:Calculate()***********************************************//功能:计算逆波兰数组double CCounter::Calculate(){ int i;//循环变量i for(i=0;i<m_sOprand.size();i++) { CString str=m_sOprand.at(i);//str用于临时存储第i个操作符 char c=str.GetAt(0);//c为第i个操作符的第一个字符 //如果c是二元运算符,且str长度为1【排除负数干扰】 if(IsOprand(c) && str.GetLength()==1) { double a=0,b=0;//用于存储用于计算的两个数 a=m_sResult.back ();//运算符前第一个数 m_sResult.pop_back ();//将运算符前第一个数弹出 b=m_sResult.back ();//运算符前第二个数 m_sResult.pop_back ();//将运算符前第二个数弹出 //判断c是哪一个运算符,并计算 switch (c) { case '+' : m_sResult.push_back (b+a); break; case '-' : m_sResult.push_back (b-a); break; case '*' : m_sResult.push_back (b*a); break; case '/' : m_sResult.push_back (b/a); break; case '^': m_sResult.push_back (pow(b,a)); break; default: break; }//end switch }//end if else //如果c不是运算符,则str为一串数字 { m_sResult.push_back(atof (m_sOprand.at(i)) );//atof函数,把CString转化成数字 } }//end for return m_sResult[0];}
一元运算符(如sin cos)等的计算函数我个人采用的方法过于复杂,希望大家能提供更简介有效的算法,代码如下:
//函数:CalculateOne**********************************************//功能:一元运算符计算void CCounter::CalculateOne(int i, CString str){//类似整个类的算法,定义相关变量 vector<CString> symbol;//运算符堆栈 vector<CString> oprand;//操作数堆栈 vector<double> result;//运算结果堆栈 symbol.push_back ("#");//符号堆栈最底端为# //将括号内的前缀式转化成逆波兰数组 while(i<m_string.GetLength() && m_string.GetAt(i)!=')') { //如果为二元运算符 if( IsOprand( m_string.GetAt(i) )) { //若后进运算符优先级高于栈顶元素,则继续进栈 if(IsPriority (m_string.GetAt(i), symbol.back())) { symbol.push_back(m_string.GetAt(i)); } else //若后进运算符优先级低于或等于栈顶元素, { oprand.push_back( symbol.back() );//栈顶元素存入逆波兰数组 symbol.pop_back ();//栈顶元素出栈 symbol.push_back(m_string.GetAt(i));//后进运算符存入运算符堆栈 }//end else }//end if //若为数字 if(m_string.GetAt(i)>='0'&& m_string.GetAt(i)<='9') { CString num="";//t为临时变量,将运算符之间的数字和小数点存为一整个字符串t while(i<m_string.GetLength()&& (m_string.GetAt(i)>='0' && m_string.GetAt(i)<='9'||m_string.GetAt(i)=='.') ) { num+=m_string.GetAt(i) ; i++; } if(i<m_string.GetLength()) { i--; }//由于在for中有i++,故先i-- oprand.push_back(num);//将由数字和小数点组成的字符串num整个存入操作数栈 }//end if i++; }// end while while(symbol.back()!='#') { oprand.push_back(symbol.back());//把运算符栈栈顶元素压入操作符栈 symbol.pop_back();//弹出运算符栈栈顶元素 }//此时堆栈symbol中只剩#,oprand即为逆波兰数组//计算括号内的表达式 int j; for(j=0;j<oprand.size();j++) { CString s=oprand.at(j);//str用于临时存储第i个操作符 char c=s.GetAt(0);//c为第i个操作符的第一个字符 if( IsOprand(c) )//如果c是二元运算符 { double a=0,b=0;//用于存储用于计算的两个数 a=result.back ();//运算符前第一个数 result.pop_back ();//将运算符前第一个数弹出 b=result.back ();//运算符前第二个数 result.pop_back ();//将运算符前第二个数弹出 //判断c是哪一个运算符 switch (c) { case '+' : result.push_back (b+a); break; case '-' : result.push_back (b-a); break; case '*' : result.push_back (b*a); break; case '/' : result.push_back (b/a); break; case '^': result.push_back (pow(b,a)); break; default: break; }//end switch }//end if else //如果c不是运算符,则str为一串数字 { result.push_back(atof (oprand.at(j)) );//atof函数,把CString转化成数字 } }//end for double number; //判断str为哪一种运算符,并将计算结果存入number if(str=="sin") { number=sin( result[0] ); } if(str=="cos") { number=cos( result[0] ); } if(str=="tan") { number=tan( result[0] ); } if(str=="negate") { number= -result[0] ; } if(str=="sqrt" && result[0]>=0) { number=sqrt(result[0]); } //将double转为CString CString cstr; cstr.Format("%g",number); loop=i;//因每个函数内的变量不能互相调用,故将处理后的i赋给变量loop m_sOprand.push_back(cstr); //将单元运算符名称压入操作数栈}
基本上就是把前面两个函数整合了一下,在此真心不推荐这么做,敬请各路大神教我简便方法。
其他的函数在此就不一一赘述,下面谈一谈二进制转换问题的实现
/*函数TransformInt()**************************************** 功能:将十进制整数数字转化成二进制*****************************/CString CBinarySystem::TransformInt(){ int num=atof(m_string);// 将CString 转化成int 型数字 int i,size = 0;//i作为循环变量,size为二进制数的长度 while(num)//当num不为0的时候 { BinaryNumber.push_back(num%2);//将num对2求余,存入堆栈中 num/=2; size++; } m_string="";//将m_string清空 for(i=0; i<size; i++)//将堆栈中的数存入m_string { m_string+=BinaryNumber.back()+48;//“0”的ASCII码是48 BinaryNumber.pop_back();//将堆栈顶层的数字删除 } return m_string;}
在此我采用了整数和小数分别调用不同函数的方法,防止混乱,当然也可以精简,合二为一。
/*函数TransformFloat()******************************************* 功能:将十进制浮点数字转化成二进制*****************************/CString CBinarySystem::TransformFloat(){ float num=atof(m_string);// 将CString 转化成float 型数字 int i = 0, size = 0, integer = 0; //i作为循环变量,size为二进制数的长度,integer为num的整数部分 integer=num;//自动强制类型转换,得到数字的整数部分 m_string="";//将m_string清空 if(integer==0)//如果整数部分为0,则为纯小数 { m_string+="0";//需在纯二进制小数前加0 } //先处理整数integer部分 while(integer)//当整数部分integer不为0的时候 { BinaryNumber.push_back(integer%2);//将integer对2求余,存入堆栈中 integer/=2; size++; } for(i=0; i<size; i++)//将堆栈中的数存入m_string { m_string+=BinaryNumber.back()+48;//“0”的ASCII码是48 BinaryNumber.pop_back();//将堆栈顶层的数字删除 }//至此,整数部分处理完毕 m_string+=".";//加上小数点 //再处理小数部分 integer=num;//自动强制类型转换,得到数字的整数部分 float decimal=num-integer;//得到数字的小数部分 while(decimal>1e-6 )//当小数部分不为0时 { //乘2取整,顺序排列"法 decimal=decimal*2;//用2乘十进制小数,得到乘积 integer=decimal;//得到乘积的整数部分 decimal=decimal-integer;//得到乘积的小数部分 m_string+=integer+48; } return m_string;}
下面谈一谈二进制转十进制的问题,由于float计算的结果与所想的不一样,导致二进制小数转十进制小数功能未能完全实现,仅仅对部分二进制小数如0.1,0.11,0.111,0.101等数字成立,如1.111不成立。
在此希望有大神可以帮忙解决这个问题
/*函数ChangetoDecimal()******************************************* 功能:将二进制数字转为十进制************************************/CString CBinarySystem::ChangetoDecimal(){ float num=atof(m_string),decimal=0; int digit = 0, intresult = 0, integer = 0, s = 1; //digit为每个位上的数字,result为最终的十进制数字, //integer为数字的整数部分,decimal为小数部分,s为2的倍数 m_string=""; //先处理二进制数的整数部分 integer=num;//自动强制类型转换,得到数字的整数部分 while(integer!=0) { digit=integer%10; integer/=10; intresult+= digit*s; s*=2; } m_string.Format("%d",intresult);//将整数部分的结果转化成String类型 //再处理二进制数小数部分 integer=num;//自动强制类型转换,得到数字的整数部分 decimal=num-integer;//得到数字的小数部分 if(decimal>1e-4)//如果小数部分不为0,则需要进行小数部分的计算 { float floatresult = 0, m=0.5; //floatresult为小数部分的二进制数,m为1/2的次方 while(decimal>1e-4) { digit=decimal*10; decimal=decimal*10-digit; floatresult+=digit*m; m/=2; } m_string=""; m_string.Format("%f",intresult+floatresult); } return m_string;}
以上就是几个核心函数的实现,如有问题,敬请指正。
- VC6.0计算器(逆波兰) 短学期作业
- 逆波兰计算器(C语言)
- 逆波兰计算器(C语言)
- (未完)逆波兰计算器功能分层:
- 简易计算器(逆波兰算法)
- 计算器(逆波兰运算表达式)
- 逆波兰计算器
- 计算器 | 逆波兰表达式
- 逆波兰实现计算器
- 简单逆波兰计算器
- 逆波兰计算器
- 逆波兰表达式计算器
- 逆波兰计算器
- 逆波兰计算器
- 逆波兰计算器
- 逆波兰计算器:
- 逆波兰计算器
- 逆波兰计算器
- Java IO系列(二):文件读取写入
- Hive SQL的编译过程
- 冒泡,选择,插入三大基本排序解析以及Demo
- 208. Implement Trie (Prefix Tree)
- 装载和初始化
- VC6.0计算器(逆波兰) 短学期作业
- (GridLayout)网格布局 :网格的世界更精彩
- 自定义View相关API
- java内存分配
- 第1次编程作业
- LayoutParams类的参数和方法
- Android 快速掌握高德地图SDK(内含实践项目)
- 1、ExtJS入门
- Codeforces Round #368-D. Persistent Bookcase-(离线建树+暴力xjb搞)/(主席树在线乱搞)