落叶 发表于 2017-1-3 14:55:05

谢谢你的分析,源码,帮我从另一个角度理解表达式解析,等我回来用vc调试理解理解。
我的乘方和阶乘的判断错了,是因为我为每个运算符都定义了一个运算等级,把乘方理解为单操数运算符了,所以把它和阶乘的等级定为同级,现重新定义了等级,问题已经解决。
对于连续乘方应该是从左向右,还是从右向左,国内几个表达式计算器也是从左向右算的,具体数学上是怎么定义的,我不清楚,所以我暂时没有调整,但如何从右向左算乘方,目前我也想到了解决办法。假如老外是对的,我再改过来。

happysxyf 发表于 2017-1-3 16:47:54

本帖最后由 happysxyf 于 2017-1-3 17:18 编辑

落叶 发表于 2017-1-3 14:55
谢谢你的分析,源码,帮我从另一个角度理解表达式解析,等我回来用vc调试理解理解。
我的乘方和阶乘的判断 ...

好的,我一直都是按照老外的优先级来的,因为那样在解决一些数学问题时很有优势。
对了,那个代码只适合gcc编译器,VC需要小改一下。逆波兰是速度最快的算法,也是内存占用最小的。
为了省事,我全部用了双目运算,即使是sin之类的也是用伪双目模拟的,这样做节省了一半代码。

落叶 发表于 2017-1-5 14:48:40

本帖最后由 落叶 于 2017-1-5 17:23 编辑

happysxyf 发表于 2017-1-3 16:47
好的,我一直都是按照老外的优先级来的,因为那样在解决一些数学问题时很有优势。
对了,那个代码只适 ...

数字帝国的表达式计算,乘方也是从右向左算,老外的是对的,我的计算器已改过来,1.1版加上批处理和赋值语句后一起更新。
你用过我的ABC公式,算式存取功能吗?我想把它取消,全部用赋值语句存储,不知道那种方法更易用?例:用A=3+5;    代替现在的算式存入功能。

另外你的代码质量很高,看的很舒服易懂,写码速度远超过我,你写码有二年了吧?或天赋太强?;P ;P ;P

happysxyf 发表于 2017-1-7 11:18:05

本帖最后由 happysxyf 于 2017-1-7 11:22 编辑

落叶 发表于 2017-1-5 14:48
数字帝国的表达式计算,乘方也是从右向左算,老外的是对的,我的计算器已改过来,1.1版加上批处理和赋 ...

我是新手,自学C语言才半年。我的是在批处理中就直接支持批量计算,可以每秒计算1000万行。
这改是我的进版,增加了错误反馈,速度比前面的代码提升10倍左右。

/*
    REVERSE POLISH EXPRESSION CALCULATOR, COPYRIGHT@2017~2019 BY HAPPYSXYF
    REVPOLISH.EXE
    VERSION 1.6
*/

#include   <stdio.h>
#include   <ctype.h>
#include    <math.h>
#include <windows.h>

/***************定义宏变量***************/
//堆栈尺寸
#defineSTACK_SIZE   1024
//帮助说明
#define HELPINFORMATION "\
REVERSE POLISH EXPRESSION CALCULATOR,COPYRIGHT@2017~2019 BY HAPPYSXYF\n\
-----------------------------------------------------------------\n\
revpolish \n\
-----------------------------------------------------------------\n\
FUNCTIONS:\n\
    pi=3.1415926535897932, e=2.7182818284590452\n\
    +, -, *, /, %, ^, !\n\
    round, floor, ceil, exp, deg, sqrt, abs, lg, ln\n\
    sin, cos, tan, arcsin, arccos, arctan\n\
    sinh, cosh, tanh, arcsinh, arccosh, arctanh\n\
-----------------------------------------------------------------\n\
2017-01-05VERSION 1.6\n"

/***************全局类变量***************/
//数学函数关键词
static const char* KEY_WORDS[]={"e", "pi", "sqrt", "lg", "ln", "sin", "cos", "tan", "arcsin", "arccos", "arctan", "deg", "abs", "round", "floor", "ceil", "exp", "sinh", "cosh", "tanh", "arcsinh", "arccosh", "arctanh", NULL};
//运算符栈
char   STACK1={0};
//逆波兰栈
char   STACK2={0};
//浮点数栈
double STACK3={0};

/***************功能函数类***************/
//阶乘函数
long long fact(long long n)
{
    return (n<2) ?1 :n*(fact(n-1));
}
//逆波兰核心
double RevPolishCore(const char* expression)
{
        char   *op=(char*)expression, *S1=STACK1, *S2=STACK2, **key, *cp, *kp;
        double *S3=STACK3, di, ni;
       
        //生成逆波兰       
        while(*op!='\0'){               
                switch(*op){
                case ' ' :
                case '\t':
                case '\r':
                case '\n':
                        //过滤空字符
                        op++;
                        continue;

                case 'a':
                case 'b':
                case 'c':
                case 'd':
                case 'e':
                case 'f':
                case 'g':
                case 'h':
                case 'i':
                case 'j':
                case 'k':
                case 'l':
                case 'm':
                case 'n':
                case 'o':
                case 'p':
                case 'q':
                case 'r':
                case 's':
                case 't':
                case 'u':
                case 'v':
                case 'w':
                case 'x':
                case 'y':
                case 'z':
                        //识别数学函数关键词
                        key=(char**)KEY_WORDS;
                        while(*key !=NULL){       
                                cp=op, kp=*key;
                                //比对关键词字母
                                while(*cp==*kp && *kp!='\0'){
                                        cp++, kp++;
                                }
                                //验证关键词结尾
                               if((*cp<'a'||*cp>'z') && (*kp=='\0')){
                                       op=cp;
                                        break;
                                }
                                key++;
                        }
                        //构建伪双目
                        if(*key !=NULL){                               
                                *(S2++)='.';
                                *(S2++)=' ';
                                //伪双目入栈
                                while('A'<=(*S1) && (*S1)<='Z'){
                                        *(S2++)=*(S1--);
                                }
                                *(++S1)=key-(char**)KEY_WORDS+65;
                                continue;
                        }else{
                                //无法识别的数学函数
                                fputs("Unrecognized math function\n", stderr);
                                exit(1);
                        }
                        break;
               
                case '(':
                        *(++S1)=*op;
                        if(*(op+1)=='-' || *(op+1)=='+'){
                                *(S2++)='0', *(S2++)=' ';
                        }
                        break;

                case ')':
                        while(*S1!='(')
                        {
                                *(S2++)=*(S1--);
                        }               
                        //舍弃'('
                        S1--;               
                        break;

                case '+':
                case '-':
                        while(S1!=STACK1 && *S1!='(')
                        {
                                *(S2++)=*(S1--);
                        }
                        *(++S1)=*op;
                        break;

                case '^':
                        //指数符
                        while('A'<=(*S1) && (*S1)<='Z')
                        {
                                *(S2++)=*(S1--);
                        }
                        *(++S1)=*op;
                        break;

                case '!':
                        //阶乘符
                        *(S2++)=*op;
                        break;

                case '%':
                case '*':
                case '/':
                        while(('A'<=(*S1) && (*S1)<='Z') ||*S1=='%' ||*S1=='*' ||*S1=='/' ||*S1=='^'){
                                *(S2++)=*(S1--);
                        }
                        *(++S1)=*op;
                        break;

                default :
                        if((*op<'0' || *op>'9') && (*op!='.')){
                                //无法识别的运算符
                                fputs("Unrecognized operator\n", stderr);
                                exit(1);
                        }
                        //浮点数入栈
                        while(('0'<=*op && *op<='9') ||*op=='.'){
                                *(S2++)=*(op++);
                        }
                        op--;
                        *(S2++)=' ';
                        break;
                }
                op++;
        }
        //收尾逆波兰
        while(S1 !=STACK1){*(S2++)=*(S1--);}
        *S2=' ';

        //计算逆波兰
        op=STACK2;
        while(*op!=' '){
                switch(*op){
                case 'A':
                        *S3=2.7182818284590452;
                        break;
                case 'B':
                        *S3=3.1415926535897932;
                        break;
                case 'C':
                        if(*S3 <0){
                                //负数没有平方根
                                fputs("Negative numbers have no square root\n", stderr);
                                exit(1);
                        }
                        *(S3-1)=sqrt(*S3);
                        S3--;
                        break;
                case 'D':
                        if(*S3 <0){
                                //负数没有对数
                                fputs("Negative numbers are not logarithmic\n", stderr);
                                exit(1);
                        }
                        *(S3-1)=log10(*S3);
                        S3--;
                        break;
                case 'E':
                        if(*S3 <0){
                                //负数没有自然对数
                                fputs("Negative numbers have no natural logarithms\n", stderr);
                                exit(1);
                        }
                        *(S3-1)=log(*S3);
                        S3--;
                        break;
                case 'F':
                        *(S3-1)=sin(*S3);
                        S3--;
                        break;
                case 'G':
                        *(S3-1)=cos(*S3);
                        S3--;
                        break;
                case 'H':
                        if(*S3==3.1415926535897932/2){
                                //π/2没有正切值
                                fputs("The pi/2 has no tangent\n", stderr);
                                exit(1);
                        }
                        *(S3-1)=tan(*S3);
                        S3--;
                        break;
                case 'I':
                        *(S3-1)=asin(*S3);
                        S3--;
                        break;
                case 'J':
                        *(S3-1)=acos(*S3);
                        S3--;
                        break;
                case 'K':
                        *(S3-1)=atan(*S3);
                        S3--;
                        break;
                case 'L':
                        *(S3-1)=(*S3)*3.1415926535897932/180.0;
                        S3--;
                        break;
                case 'M':
                        *(S3-1)=(*S3<0)?(-(*S3)):(*S3);
                        S3--;
                        break;
                case 'N':
                        *(S3-1)=round(*S3);
                        S3--;
                        break;
                case 'O':
                        *(S3-1)=floor(*S3);
                        S3--;
                        break;
                case 'P':
                        *(S3-1)=ceil(*S3);
                        S3--;
                        break;
                case 'Q':
                        *(S3-1)=exp(*S3);
                        S3--;
                        break;
                case 'R':
                        *(S3-1)=sinh(*S3);
                        S3--;
                        break;
                case 'S':
                        *(S3-1)=cosh(*S3);
                        S3--;
                        break;
                case 'T':
                        *(S3-1)=tanh(*S3);
                        S3--;
                        break;
                case 'U':
                        *(S3-1)=asinh(*S3);
                        S3--;
                        break;
                case 'V':
                        *(S3-1)=acosh(*S3);
                        S3--;
                        break;
                case 'W':
                        *(S3-1)=atanh(*S3);
                        S3--;
                        break;
                case '+':
                        *(S3-1)+=*S3;
                        S3--;
                        break;
                case '-':
                        *(S3-1)-=*S3;
                        S3--;
                        break;
                case '*':
                        *(S3-1)*=*S3;
                        S3--;
                        break;
                case '%':
                case '/':
                        if(*S3 !=0){
                                if(*op=='%'){
                                        //取余数
                                        *(S3-1)=(int)*(S3-1) % (int)*S3;
                                }else{
                                        *(S3-1)/=*S3;
                                }
                               
                        }else{
                                //除数不能为零
                                fputs("Divisor is zero error\n", stderr);
                                exit(1);
                        }
                        S3--;
                        break;
                case '^':
                        if(*(S3-1)==0 && *S3<0){
                                //除数不能为零
                                fputs("Function pow's divisor is zero error\n", stderr);
                                exit(1);
                        }
                        *(S3-1)=pow(*(S3-1), *S3);
                        S3--;
                        break;
                case '!':
                        if(*S3 <0){
                                //负数没有阶乘
                                fputs("Negative numbers have no factorial\n", stderr);
                                exit(1);
                        }
                        *S3=fact((long long)(*S3));
                        break;
                default :
                        //字符串转浮点
                        di=0, ni=1;
                        while('0'<=*op && *op<='9'){
                                di=10*di+(*op)-'0';
                                op++;
                        }       
                        if(*op=='.'){
                                op++;
                                while('0'<=*op && *op<='9'){
                                        di=10*di+(*op)-'0';
                                        op++, ni*=10;
                                }       
                        }
                        *(++S3)=di/ni;
                        break;
                }
                op++;
        }

        /////////////////////////////////////////////////////////////////////
        //返回计算结果
        //return *S3;

        //打印中缀式
        fprintf(stdout, "ORIGINALEXP: %s\n", expression);

        //打印后缀式
        fprintf(stdout, "REVPOLISH:   ");
        op=STACK2;
        while(op!=S2){
                if(*op=='.' && *(op+1)==' '){
                        op++;

                }else if('A'<=(*op) && (*op)<='Z'){
                        fprintf(stdout, "%s ", KEY_WORDS[*op-65]);
                       
                }else{
                        fputc(*op, stdout);
                        if(*op=='+' ||*op=='-' ||*op=='*' ||*op=='/' ||*op=='%' ||*op=='^' ||*op=='!'){fputc(' ', stdout);}
                }
                op++;
        }
        fputc('\n', stdout);

        //打印计算结果
        fprintf(stdout, "RESULT:      %.16lf\n", *S3);
}

/*************MAIN主函数入口*************/
int main(int argc, char** argv)
{
        if((argc==1) || (argc==2 && argv=='/' && (argv=='?'||argv=='h'))){
                //使用说明
                fputs(HELPINFORMATION, stderr);
                exit(1);
        }
        RevPolishCore(argv);
        return 0;
}

落叶 发表于 2017-1-7 11:41:44

本帖最后由 落叶 于 2017-1-7 13:05 编辑

学了半年就能这水平,只能让我汗颜,我虽然去年才开始学写代码,但之前可是看了十多年计算机方面的书,学的广而泛,
看你的新代码加入了输入值校验功能,看来你也意识到这方面的重要性,我为我的计算器加这个功能可是用了一个多月,
就这样windows的异常处理我还没尝试过,因为关于计算器测试方面的资料没查到。
整个计算器的最终测试用了快两月,才有了一个大致的头绪,里面还没考虑到的问题还多多。
在表达式中支持空格,回车换行,并在解析表达式前除去你也采用了,这点可以增加用户使用体验。

happysxyf 发表于 2017-1-7 13:39:26

落叶 发表于 2017-1-7 11:41
学了半年就能这水平,只能让我汗颜,我虽然去年才开始学写代码,但之前可是看了十多年计算机方面的书,学的 ...

是的,基本的计算器结构都差不多,都是逆波兰式。你的计算器关键是支持大数和高精度,这点就非常了不起,还有你支持函数定义吗?比如定义 f(x)=sin(x)/x;直接输入f(3.5)求值。

落叶 发表于 2017-1-7 13:47:35

本帖最后由 落叶 于 2017-1-7 14:31 编辑

不支持,高精度这块也没什么要做的,主要定位是用户体验。其实这个计算器最难的是高精度和表达式运算结合在一起。这个计算器最难的是这个框架的建立。
如果只是你所述的函数定义,我考虑了一下,可以快速实现,因为它的核心也是表达式计算,而这个是我计算器的主要功能。

happysxyf 发表于 2017-1-7 14:04:44

落叶 发表于 2017-1-7 13:47
不支持,高精度这块也没什么要做的,主要定位是用户体验。其实这个计算器最难的是高精度和表达式运算结合在 ...

要是能绘图就更好了

落叶 发表于 2017-1-7 14:45:52

本帖最后由 落叶 于 2017-1-7 14:51 编辑

我的算式存入功能也可实现函数功能,只是不直观,多看看帮助,刚乱码了,我举的例子乱码了。以后增加赋值语句后会有所改观的。

落叶 发表于 2017-1-7 17:17:57

happysxyf 发表于 2017-1-7 14:04
要是能绘图就更好了

那个绘图就没办法了,那需要团队合作,我既使免强做出一点,也会画虎不成反类猫,个人开发,只能专而精,知识也跟不上。

想请问一下你,你的关键字识别所用方法是你自己所想的吗?比我的方法好多了,我要借荐你的方法。
页: 1 2 [3] 4
查看完整版本: 落叶高精度表达式计算器v1.0版