VC6.0计算器(逆波兰) 短学期作业

学校短学期的作业,最初没有头绪的时候参考了 新一凡的博客
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;
}

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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值