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;}

以上就是几个核心函数的实现,如有问题,敬请指正。

0 0
原创粉丝点击