学校短学期的作业,最初没有头绪的时候参考了 新一凡的博客
http://blog.csdn.net/u013240179/article/details/39105843
上对变量的一些设置以及部分算法,对此表示十分感谢。
同时我在此基础上做出了许多改进。
使用的是Visual C++ 6.0
采用的是逆波兰算法
实现了:
1含有括号的加减乘除运算;
2sin、cos、tan、开方、幂函数等运算;
3十进制数转二进制数(整数和小数);
4二进制转十进制(整数)(由于浮点数计算问题小数转换未能实现);
首先,是创建工程文件
选择 MFC AppWizard(exe),输入工程名称,点击创建,
选择单文档(single document),然后一直点下一步,如图所示
工程创建好后,再添加类,再次就不一一赘述。
绑定的变量在此初始化为空
/////////////////////////////////////////////////////////////////////////////
// CMycalculatorDlg dialog
CMycalculatorDlg::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 here
m_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;
}
以上就是几个核心函数的实现,如有问题,敬请指正。