落叶
发表于 2016-12-28 19:57:14
本帖最后由 落叶 于 2016-12-28 19:59 编辑
happysxyf 发表于 2016-12-28 19:14
表达式解析,最好还是用逆波兰特最简单,速度最快,内存占用最小,我实现表达式计算只要3小时。但是我不 ...
天哪,你表达式实现只要了三小时,你知道我构思基本通过用了多少?用了两个月,中间完善大概四个月吧,就这样我还觉得不可思议,感觉太快了!
happysxyf
发表于 2016-12-29 12:02:03
落叶 发表于 2016-12-28 19:33
http://bbs.emath.ac.cn/thread-8882-2-1.html这里有我向宝宝请教对数的贴子,我的主题里有对三角函数的学 ...
好的,感谢提供思路,因为逆波兰特是几乎所有现代计算机的实现方式。太经典,所以必须得用。
落叶
发表于 2016-12-29 17:20:36
本帖最后由 落叶 于 2016-12-29 20:15 编辑
我已过了需要表扬的年龄了,首先在这里第一个发布,就是来让网友们批评,指正的,错误啥的大量接收!
happysxyf
发表于 2016-12-30 19:02:44
本帖最后由 happysxyf 于 2016-12-30 19:06 编辑
落叶 发表于 2016-12-29 17:20
我已过了需要表扬的年龄了,首先在这里第一个发布,就是来让网友们批评,指正的,错误啥的大量接收!
落叶,你的计算器非常好用,暂无漏洞。圆周率很快,准确无误。
但是居然没有排列组合数,这个经常需要用到。
落叶
发表于 2016-12-31 02:00:41
本帖最后由 落叶 于 2016-12-31 02:05 编辑
整体框架现比较完善,加功能很方便的,批量计算已想好,1.1版再加上,发现一个问题,x的负一次方忘了判断,现计算是错的。
那个圆周率我没有计算,存了一个万位常数直接读取。
happysxyf
发表于 2016-12-31 11:02:17
落叶 发表于 2016-12-31 02:00
整体框架现比较完善,加功能很方便的,批量计算已想好,1.1版再加上,发现一个问题,x的负一次方忘了判断, ...
怪不得计算派都不需要时间,直接就出来了。:L
落叶
发表于 2017-1-2 15:47:51
本帖最后由 落叶 于 2017-1-2 19:56 编辑
刚才简单的和SpeedCrunch(老外的成品计算器)在表达式解析方面作了一下测试,(国内的还没找到合适的)
sin(5)
= -0.95892427466313846889
((((((((2^2)*2)^2)*2)^2)*2)^2)^2)*2
= 2305843009213693952
-((((((((2^2)*2)^2)*2)^2)*2)^2)^2)*2
= -2305843009213693952
23+56/(102-100)*((36-24)/(8-6))
= 191
1.55- 1.55^3/3! +1.55^5/5! - 1.55^7/7!+ 1.55^9/9!
= 0.99978682495119270984
1.55- 1.55*1.55^2*(4*5*6*7*8*9 - 1.55^2*(6*7*8*9 - 1.55^2*(8*9- 1.55^2)))/9!
= 0.99978682495119270984
sin(6)+3
= 2.72058450180107412719
cos(sin(6)^4)^6
= 0.99988854394413890284
cos (sin(6)^ 4)^ 6
= 0.99988854394413890284
cos(sin(-6)^4)^6
= 0.99988854394413890284
cos(-sin(6)^4)^6
= 0.99988854394413890284
2^cos(sin(6)^4)^6
= 1.9998454950664980933
上面有红色部分和我的不一致:
我的是:2^cos(sin(6)^4)^6=6.3995055612514362198736651007398E1
然后加上括号后:2^(cos(sin(6)^4)^6 )=1.9998454950664980933018590689861,这时才和它的结果保持一致,
不知道从语法上到底是谁才是对的?
SpeedCrunch的:2^sin(3)^2= 1.01389964085539956385,我的:2^sin(3)^2=1.216081581587201296984729037121值不同
2^sin(3)*2= 2.20552178097356481036但这个数 SpeedCrunch的和我一致,看来它采用的是乘方指数优先原则。
落叶
发表于 2017-1-2 16:36:01
本帖最后由 落叶 于 2017-1-2 21:46 编辑
在算4^4!时:
SpeedCrunch的是4^4!= 281474976710656,很明显它是先算了后面的。
我目前把两个运算符的等级定为相等,所以我的是左边先算:4^4! =8.5781777534284265411908227168123E506
不知道两个运算符的数学运算等级是怎么样的,上网查了一下也没查出结果?
单操作数的运算等级应该高于双操作数的,看来它是对的,我的运算符等级需要重新调整一下。开始我把乘方运算定义为单操作数了:L
又查了一个国内比较牛的人写的表达式计算,运算4^4!采用的规则和它的一样。看来我需要改一下。好了,调了一下运算符等级,现在这个问题已解决。
落叶
发表于 2017-1-2 17:03:14
本帖最后由 落叶 于 2017-1-2 21:19 编辑
SpeedCrunch的3^4^2= 43046721
我的是:3^4^2=6.561E3
难道两个乘方符号在一起先算右边的?
不过在我的计算器上有个等级最高的括号,若为了表达清晰,请把需要先算的括起来,就可以正常运算了。
又查了几个国内比较牛的人写的表达式计算,采用连续乘方的规则和我的一样。
happysxyf
发表于 2017-1-3 13:20:54
本帖最后由 happysxyf 于 2017-1-3 17:25 编辑
落叶 发表于 2017-1-2 17:03
SpeedCrunch的3^4^2= 43046721
我的是:3^4^2=6.561E3
难道两个乘方符号在一起先算右边的?
如果你使用逆波兰,你就没有这些问题了,我又花了几小时写了个逆波兰,这个的计算结果跟SpeedCrunch软件的完全吻合,运算优先级完全一致。只要你用逆波兰那些优先级都是一致的。只是我没加入高精度,其实大体框架已经成了。就差做个计算器的界面和按钮 以及 调用GMP的大数库提高点精度。
我的代码,本来只有100行,为了弄精致点变成了/*
REVERSE POLISH EXPRESSION CALCULATOR, COPYRIGHT@2017~2019 BY HAPPYSXYF
REVPOLISH.EXE
VERSION 1.0
*/
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <windows.h>
/***************定义宏变量***************/
//堆栈尺寸
#defineSTACK_SIZE 1024
//数学函数关键词数
#defineKEY_SIZE 23
//帮助说明
#define HELPINFORMATION "\
REVERSE POLISH EXPRESSION CALCULATOR, VERSION 1.0\n\
-----------------------------------------------------------------\n\
revpolish \n\
-----------------------------------------------------------------\n\
EXAMPLES:\n\
revpolish ((3*3+2)%6+7.187)*5-7/189+3^2\n\
revpolish (ceil(sin(pi/3)+2)%6+0.187)*e-lg(6.5)\n\
revpolish 5*(arctan(cos(sin(ln(lg(2.71828))))))\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\
COPYRIGHT@2017~2019 BY HAPPYSXYF 2017-01-03\n"
/***************全局类变量***************/
//数学函数关键词
static const char* KEY_WORDS[]={"arcsinh", "arccosh", "arctanh", "arcsin", "arccos", "arctan", "round", "floor", "ceil", "sqrt", "deg", "sinh", "cosh", "tanh", "lg", "ln", "sin", "cos", "tan", "abs", "exp", "e", "pi"};
static const charKEY_LLENS[]={ 7, 7, 7, 6, 6, 6, 5, 5, 4, 4, 3, 4, 4, 4, 2, 2, 3, 3, 3, 3, 3, 1, 2 };
//运算符栈
char STACK1={0};
//逆波兰栈
char STACK2={0};
//浮点数栈
double STACK3={0};
/***************功能函数类***************/
//判断逆波兰函数符,即大写字母
inline BOOL isLETTER(const char c)
{
if('A'<=c && c<='Z'){return TRUE;}
return FALSE;
}
//判断浮点数,包括小数点
inline BOOL isDDIGIT(const char c)
{
if( ('0'<=c&&c<='9') || c=='.'){return TRUE;}
return FALSE;
}
//阶乘函数
int fact(int n)
{
return (n<2) ?1 :n*(fact(n-1));
}
//识别关键词
char IdentifyKeyWords(char* pstr)
{
char SN;
for(SN=0; SN<KEY_SIZE; SN++){
char *op=pstr, *kp=(char*)KEY_WORDS;
//比对字母
while(*op==*kp && *kp!='\0'){
op++, kp++;
}
//尾部验证
if((*op<'a'||*op>'z') && (*kp=='\0')){
return SN;
}
}
return -1;
}
//逆波兰核心
void RevPolishCore(const char* expression)
{
char *op=(char*)expression, *S1=STACK1, *S2=STACK2, SN;
double *S3=STACK3;
//逆波兰化
while(*op!='\0')
{
if(isspace(*op)){
op++;
continue;
}
//判断伪双目
if( (SN=IdentifyKeyWords(op)) !=-1)
{
*(S2++)='.';
*(S2++)=' ';
//伪双目入栈
while(isLETTER(*S1))
{
*(S2++)=*(S1--);
}
*(++S1)=SN+65;
op+=KEY_LLENS;
continue;
}
switch(*op)
{
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(isLETTER(*S1))
{
*(S2++)=*(S1--);
}
*(++S1)=*op;
break;
case '!':
*(S2++)=*op;
break;
case '%':
case '*':
case '/':
while(isLETTER(*S1) ||*S1=='%' ||*S1=='*' ||*S1=='/' ||*S1=='^')
{
*(S2++)=*(S1--);
}
*(++S1)=*op;
break;
default :
//浮点数入栈
while(isDDIGIT(*op)){
*(S2++)=*(op++);
}
op--;
*(S2++)=' ';
break;
}
op++;
}
while(S1 !=STACK1)
{
*(S2++)=*(S1--);
}
*S2=' ';
//计算逆波兰
op=STACK2;
while(*op!=' ')
{
//计算伪双目
if(isLETTER(*op))
{
switch(*op)
{
case 'A':
*(S3-1)=asinh(*S3);
break;
case 'B':
*(S3-1)=acosh(*S3);
break;
case 'C':
*(S3-1)=atanh(*S3);
break;
case 'D':
*(S3-1)=asin(*S3);
break;
case 'E':
*(S3-1)=acos(*S3);
break;
case 'F':
*(S3-1)=atan(*S3);
break;
case 'G':
*(S3-1)=round(*S3);
break;
case 'H':
*(S3-1)=floor(*S3);
break;
case 'I':
*(S3-1)=ceil(*S3);
break;
case 'J':
*(S3-1)=sqrt(*S3);
break;
case 'K':
*(S3-1)=(*S3)*3.1415926535897932/180.0;
break;
case 'L':
*(S3-1)=sinh(*S3);
break;
case 'M':
*(S3-1)=cosh(*S3);
break;
case 'N':
*(S3-1)=tanh(*S3);
break;
case 'O':
*(S3-1)=log10(*S3);
break;
case 'P':
*(S3-1)=log(*S3);
break;
case 'Q':
*(S3-1)=sin(*S3);
break;
case 'R':
*(S3-1)=cos(*S3);
break;
case 'S':
*(S3-1)=tan(*S3);
break;
case 'T':
*(S3-1)=(*S3<0)?(-(*S3)):(*S3);
break;
case 'U':
*(S3-1)=exp(*S3);
break;
case 'V':
*S3=2.7182818284590452;
break;
case 'W':
*S3=3.1415926535897932;
break;
default :
break;
}
if(*op!='V' && *op!='W'){S3--;}
op++;
continue;
}
//计算双目
switch(*op)
{
case '+':
*(S3-1)+=*S3;
S3--;
break;
case '-':
*(S3-1)-=*S3;
S3--;
break;
case '*':
*(S3-1)*=*S3;
S3--;
break;
case '%':
case '/':
if(S3 !=STACK3){
if(*op=='%'){
*(S3-1)=(int)*(S3-1) % (int)*S3;
}else{
*(S3-1)/=*S3;
}
}else{
//除数为0,抛出错误并退出
fputs("Divisor is zero error\n", stderr);
exit(0);
}
S3--;
break;
case '^':
*(S3-1)=pow(*(S3-1), *S3);
S3--;
break;
case '!':
*S3=fact((int)(*S3));
break;
default :
//集数器
if (isDDIGIT(*op)){
*(++S3)=atof(op);
}
while(isDDIGIT(*op)){
op++;
}
break;
}
op++;
}
//打印中缀式
fprintf(stdout, "ORIGINALEXP: %s\n", expression);
//打印后缀式
fprintf(stdout, "REVPOLISH: ");
op=STACK2;
while(op!=S2){
if(*op=='.' && *(op+1)==' '){
op++;
}else if(isLETTER(*op)){
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){
//异常则抛出使用说明
fputs(HELPINFORMATION, stderr);
exit(1);
}
RevPolishCore(argv);
return 0;
}
300行:
以下是说明
用法:
-----------------------------------------------------------------------------
revpolish
-----------------------------------------------------------------------------
示例:
revpolish ((3*3+2)%6+7.187)*5-7/189+3^2
revpolish (ceil(sin(pi/3)+2)%6+0.187)*e-lg(6.5)
revpolish 5*(arctan(cos(sin(ln(lg(2.71828))))))
-----------------------------------------------------------------------------
备注:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
常数类
pi 3.1415926535897932
e 2.7182818284590452
通用类
round 四舍五入
floor 整取
abs 绝对值
ceil向上舍入
deg 角度转弧度
exp e的次幂
sqrt开方
fac 阶乘
lg 常用对数,以10为底
ln 自然对数
+ 加
- 减
* 乘
/ 除
% 取余数
^ 次方
! 阶乘
三角函数类
sin、cos、tan
arcsin、arccos、arctan
双曲函数类
sinh、cosh、tanh
arcsinh、arccosh、arctanh
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
源码都是完整的,直接就能编译,如果你的机器无法编译C语言请下载我的附件。我就差个高精度和计算器按钮界面了。