万位计算器资料.docx
《万位计算器资料.docx》由会员分享,可在线阅读,更多相关《万位计算器资料.docx(53页珍藏版)》请在冰豆网上搜索。
![万位计算器资料.docx](https://file1.bdocx.com/fileroot1/2022-11/23/53803c27-c014-46a6-ae4a-808a4e96706d/53803c27-c014-46a6-ae4a-808a4e96706d1.gif)
万位计算器资料
万位计算器
摘要
设计思路
软件架构
实现原理与过程
软件测试
实验心得与感悟
附件&代码
摘要
这是一个用java开发的高精度计算器,部署在Android平台上,该计算器支持万位数的运算(但说实话,万位的运算是理论上的,毕竟现实生活中我们能用到百位就很不错了,不过这个计算器还是按照万位的标准来做的),实现了,加、减、乘、除、正弦、余弦、指数幂、对数等一系列常用运算,其中正弦、余弦、指数幂、对数等函数型的运算均采用迈克劳林展开式进行计算,提高了计算效率,同时也增强了计算精度。
另外,本计算器利用Poland后缀表达式实现了多项式的运算,使得计算器的人性化程度极大的提高,大大的增加了计算器的实用性。
同时,该计算器部署到安卓平台,对于安卓手机应用的比较广泛的今天,(至少以后我自己可以用这个APP来处理我自己的一些日常计算问题,这样的话,虽然计算机组成与原理这门课程结课了,但我的作业并未束之高阁,可以实际用起来),该计算器或许会发挥一定的作用呢,我已经将该计算器的源码放在了GitHub上了,为中国的开源做一份力量,希望我的作品会给别人一些启示和帮助,也希望有人能对我的作品进行进一步的改进与完善。
当然,这个计算器的部署平台不仅仅是android,在pc端也有其可执行的文件,只不过,pc端我仅仅做了一个控制台而已,但是这足以说明我自己写得java大数据运算包能在各个平台下通用,具有较为健壮的能力。
关键词:
高精度计算器、运算、多项式运算、android
设计思路
在设计完成这个计算器之前,我主要处于以下几点来考虑:
1.这个计算器应该具有通用型,即无论在windows下还是在linux下,或者还是在android平台下,都可以执行
2.该计算器应该能够快速高效的实现指数幂,对数等不较复杂的运算。
3.能够支持多项式运算,这样可以增加其实用性
4.应该具有很好的容错功能,不然软件就没法用了。
对于以上的四点考虑,我的解决方案如下:
1.使用java作为开发语言,java是运行在java虚拟机之上的,具有很高的通用性
2.采用迈克劳林公式进行指数对数运算,将极大提高运行的效率和精度
3.采用Poland的后缀表达式,使得计算器支持多项式运算
4.加入出错处理模块,让程序在运行过层中自动的进行出错处理
软件架构
我的万位计算器的基本结构如下:
万位计算器基本模块图
计算器总的分为以上三个模块,各个模块都有各自独立实现的功能:
1.大数据处理模块:
这个模块主要是定义我的大数的数据结构,以及完成基于这个数据结构的各种运算,如加减乘除正弦余弦模幂对数等一系列运算
2.表达式处理模块:
这个模块主要是将用户的输入进行检错和转换,将正确的中缀表达式转换成后缀表达式,然后进行求解后缀表达式,最终得到结果
3.界面模块:
这个模块主要是用于与用户交互,良好的界面能够增加应用的实用性,它是连接用户与底层实现的桥梁
总的说来,架构还是相当清晰的,并且根据这个架构进行开发,使得我的开发效率大大提高。
实现原理与过程
这个部分将较为详细的讲述,按照三个模块的顺序进行分析。
(一)大数据处理模块
数据结构:
publicBigDouble(){
dBaseNumber=0.0;
iIndexNumber=0;
}
上面是最基本的数据结构,分为指数与底数两个模块,全程采用科学计数法进行保存数据。
这样做的好处是可以尽量使得运算变得简单有效,但是却损失了精度,当然,后来我将double替换成了java自带的BigDecimal类,使得进度一下子提高,但是,运行速度一下子变慢了,由于考虑到我要将这个程序写到手机应用里去,效率应该多考虑一点,毕竟,只要通过这个编程,我对计算机对数据的处理的理解跟进一步就行了,我更多想的是这个软件以后我自己还会用,也许说不一定别人也可以用,所以我最终还是选择了double作为底数。
下面是改模块实现的函数列表:
ModifierandType
MethodandDescription
BigDouble
log10(BigDoubleotherBigDouble)
返会以10为底,otherBigDouble为真值的对数
BigDouble
ln(BigDoubleotherBigDouble)
返会以e为底,otherBigDouble为真值的对数
BigDouble
bigAbs(BigDoubleotherBigDouble)
返回otherBigDouble的绝对值
BigDouble
bigSqrt(BigDoubleotherBigDouble)
返回对otherBigDouble的平方根
BigDouble
bigExp(BigDoublebDouble1)
返回e的bDouble1次方
BigDouble
add(BigDoubleotherBigDouble)
返回this和otherBigDouble的加法结果
BigDouble
minus(BigDoubleotherBigDouble)
返回this和otherBigDouble的减法结果
BigDouble
multi(BigDoubleotherBigDouble)
返回this*otherBigDouble
BigDouble
div(BigDoubleotherBigDouble)
返回this/otherBigDouble
BigDouble
sin(BigDoubleotherBigDouble)
返回sin(otherBigDouble)
BigDouble
cos(BigDoubleotherBigDouble)
返回cos(otherBigDouble)
BigDouble
pow(BigDoublebDouble1,BigDoublebDouble2)
返回bDouble1^bDouble2
BigDouble
tan(BigDoubleotherBigDouble)
返回tan(otherBigDouble)
上述涉及的函数型运算全部采用迈克劳林展开式进行运算,
如:
等,这里就不再一一列举了,总之采用麦克劳林方法计算,将指数转化为多项式运算,大大提高了效率。
(二)表达式处理模块
这一块其实算起来是最难的,首先得对输入的字符串表达式进行正确性判断,然后还要将正确的中缀表达式转换成后缀表达式,然后进行计算。
这一块,至今为止,出错控制我仍然没有做的很到位。
该模块的主要流程如下:
使用波兰表达式进行求解,第一步要将表达式拆分成各个基本元素,第二步对拆分成基本元素的中缀表达式进行排序,将其转化为后缀表达式,第三步从左到右依次计算,直到遇到表达式结尾符,输出结果
下面是一个利用中缀表达式求解的样例:
中缀表达式的计算原理是这样的:
将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
(1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
(2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
(3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
(4)如果不是数字,该字符则是运算符,此时需比较优先关系。
做法如下:
将该字符与运算符栈顶的运算符的优先关系相比较。
如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。
倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。
(5)重复上述操作(3)-(4)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
最后,计算后缀表达式的时候如果遇到数字就放到栈里,遇到操作符就根据操作符是单目还是双目运算符从栈里取出相应的数据,将计算结果入栈,知道扫描完整个后缀表达式队列后,结果栈内的数据就是表达式的值。
比如:
5-(2+1)*12.1
后缀:
521+12.1*-
运算符的优先级
符号
优先级
#(定义的特殊符号,队列计算中作为标志,无实际意义)
0
+
1
-
1
*
2
/
2
^
3
sin
4
cos
4
tan
4
cot
4
asin
4
acos
4
acot
4
atan
4
log
4
ln
4
exp
4
abs
4
sqrt
4
(
5
)
5
代码这里就不展示了,代码全部在附件之中,反正就是按照这个流程走的。
(三)用户界面模块:
说实话,界面模块其实是相对简单的,采用了xml超文本进行UI的设计,使得界面开发比较方便,主要特点是部署到android平台:
下面是布局文件的代码片段:
android:
id="@+id/rstText"
android:
layout_width="match_parent"
android:
layout_height="100dp"
android:
layout_alignLeft="@+id/expText"
android:
layout_alignParentTop="true"
android:
layout_marginTop="23dp"
android:
gravity="bottom|right"
android:
textSize="18sp"
android:
textColor="#FFF"
android:
background="@drawable/edit"
android:
editable="false"
android:
ems="10"/>
android:
id="@+id/expText"
android:
layout_width="match_parent"
android:
layout_height="100dp"
android:
layout_below="@+id/rstText"
android:
background="@drawable/edit"
android:
ems="10"
android:
gravity="bottom|right"
android:
hint=""
android:
textColor="#FFF"
android:
textSize="25sp"
android:
editable="false"
>
下面是实际视图:
软件测试
简单做了一些测试,由于采用的是麦克劳林展开式,故有些数据并不是完全是理论上那样,会有一定的损失,这个我已经提前预料到了,比如sin(pi):
但是在理论上仍是正确的,
多项式运算也能够支持:
总之,测试上能够达到预期的目的
实验心得与总结
总的说来,通过这次编程实践,我学到了很多知识,过程中也遇到了很多困难,但是都一一进行了解决,能力得到了较大的提高。
首先最开始我在开始做这个软件之前我就在想,我把这个做完之后又能怎样呢,因为我遇到过很多这样的事,我们有时候做了很多事,但是做完之后就扔那,算是完成任务就再也不管了,但是我不想这样,我想的是我不仅要学到知识,我还要我做的东西以后能够用起来,至少可以供我自己使用,结合这个想法,加之一直对手机应用程序开发一直有点兴趣,但是就是没有合适的机会进行学习,所以这次我选择了android平台下的开发,这样的话,我既可以学习计算机组成与原理,又可以学习手机应用开发,还可以使自己做的东西可以得到应用,这样的感觉很不错。
于是,就这样,我就开始开始做这个手机端的高精度计算器了
从一开始开发的路途就不平坦,准确的说是不怎么平坦,最开始在数据结构方面就来来回回的改,好不容易定下来了,然后到后面开发优势磕磕绊绊,特别是在做麦克劳林时,由于double本身的限制,我一度被卡在精度这一块。
当看着旁边有的同学用python的自带大数已经早早就做完这个计算器时,我变得很着急,因为我的核心类还没完成,但是,我还是坚持了下来,终于,进过一两周的艰苦奋斗,我把大数的核心类写完了。
这个时候,我面临的是做不做多项式运算,因为之前进度在我前面的好几个人都说多项式运算太难,开始是想做,但是最后就只做了一个一次只能有一个运算符的计算器,我当时也有点畏难,本打算也就不做多项式运算了,但是后来又想想,如果多项式运算都不支持,那何来实用性,谁会用一个连用着都不舒服的应用,用户才不管你程序员开发时难不难呢,用户要的是使用体验,如果程序员不能给用户良好的体验,那就不说一个合格的程序员,于是我下定决心要把多项式这一块啃下来,于是我到图书馆借了一些书,并结合不联网进行了这一方面的学习,通过一周左右的时间,我掌握了两种比较好的方法,一种是二叉树计算,另一种就是逆波兰后缀表达式计算,我比较喜欢后者,于是,在前面的基础上,我花了两天就把它写了出来,心里想想,也不是那么难嘛,到了最后一个阶段,就是android界面设计这个模块,虽然界面是比较简单的事,但想做好却并不容易,我从亚马逊上买了一本专门讲androidUI设计的书,边学边开发,于是,最后我的app就出来了。
虽然说只是做了一个app,但是,这个过程中收获实在太多了,不一而足,不是三言两语就能说明白的,这种乐趣,只能在切切实实的实践中才能切身感受得到。
附件&代码
(一)大数据处理模块:
packagecom.example.big;
publicclassBigDouble{
privatestaticfinalintINF=100;//无穷大,百位计算器
privatestaticfinaldoublePI=3.141592653589793;
privatestaticfinaldoubleEXP=2.718281828459046;
privatedoubledBaseNumber;
privateintiIndexNumber;
/*
*一组构造函数
**/
publicBigDouble(){
dBaseNumber=0.0;
iIndexNumber=0;
}
publicBigDouble(intiNumber){
StringnumberStr=String.valueOf(iNumber);
intiLength=numberStr.length();
intiDotIndex=iLength;
intiFirstNoZeroNumberIndex=iLength;
charnumberStrArray[]=numberStr.toCharArray();
for(inti=iLength-1;i>=0;--i){
if(numberStrArray[i]=='.'){
iDotIndex=i;//小数点的位置
}
if(numberStrArray[i]>'0'&&numberStrArray[i]<='9'){
iFirstNoZeroNumberIndex=i;//第一个非零数的位置
}
}
if(iDotIndexthis.iIndexNumber=iDotIndex-iFirstNoZeroNumberIndex;
}
else{//1.01型
this.iIndexNumber=iDotIndex-iFirstNoZeroNumberIndex-1;
}
doubledNumber=Double.parseDouble(numberStr);
doubledTemp=0.0;
intindex=0;
if(dNumber==0.0){
this.dBaseNumber=dNumber;
this.iIndexNumber=0;
}
else{
for(index=-INF;indexdTemp=dNumber/Math.pow(10.0,index);
if(Math.abs(dTemp)>=1.0&&Math.abs(dTemp)<10.0){
this.dBaseNumber=dTemp;
//this.iIndexNumber=index;
break;
}
}
}
}
publicBigDouble(doubledNumber){
intindex=0;
doubledTemp=0.0;
if(dNumber==0.0){
this.dBaseNumber=dNumber;
this.iIndexNumber=0;
}
else{
for(index=-INF;indexdTemp=dNumber/Math.pow(10.0,index);
if(Math.abs(dTemp)>1.0&&Math.abs(dTemp)<10.0){
this.dBaseNumber=dTemp;
this.iIndexNumber=index;
break;
}
}
}
}
publicBigDouble(StringnumberStr){
intiLength=numberStr.length();
intiDotIndex=iLength;
intiFirstNoZeroNumberIndex=iLength;
charnumberStrArray[]=numberStr.toCharArray();
for(inti=iLength-1;i>=0;--i){
if(numberStrArray[i]=='.'){
iDotIndex=i;//小数点的位置
}
if(numberStrArray[i]>'0'&&numberStrArray[i]<='9'){
iFirstNoZeroNumberIndex=i;//第一个非零数的位置
}
}
if(iDotIndexthis.iIndexNumber=iDotIndex-iFirstNoZeroNumberIndex;
}
else{//1.01型
this.iIndexNumber=iDotIndex-iFirstNoZeroNumberIndex-1;
}
doubledNumber=Double.parseDouble(numberStr);
doubledTemp=0.0;
intindex=0;
if(dNumber==0.0){
this.dBaseNumber=dNumber;
this.iIndexNumber=0;
}
else{
for(index=-INF;indexdTemp=dNumber/Math.pow(10.0,index);
if(Math.abs(dTemp)>=1.0&&Math.abs(dTemp)<10.0){
this.dBaseNumber=dTemp;
//this.iIndexNumber=index;
break;
}
}
}
}
publicBigDouble(doubledNumber,intiNumber){
this.dBaseNumber=dNumber;
this.iIndexNumber=iNumber;
this.normalization();
}
/*
*一些内部使用的函数
**/
//返回大数的字符串
publicStringgetBigDoubleStr(){
StringbigDoubleStr=String.valueOf(this.dBaseNumber);
if(this.iIndexNumber<0){
bigDoubleStr=bigDoubleStr.concat("e");
bigDoubleStr=bigDoubleStr.concat(String.valueOf(this.iIndexNumber));
}
else{
bigDoubleStr=bigDoubleStr.concat("e+");
bigDoubleStr=bigDoubleStr.concat(String.valueOf(this.iIndexNumber));
}
returnbigDoubleStr;
}
//返回非科学计数法的大数
publicStringtoString(){
StringsNumber=Double.toString(Math.abs(this.getBaseNumber()));
charsNumberArray[]=sNumber.toCharArray();
intiIndex=this.getIndexNumber();
if(this.getBaseNumber()<0.0){
sNumber="-";
}
else{
sNumber="";
}
if(iIndex>=0){
for(inti=0;iif(sNumberArray[i]!
='.'){
sNumber+=String.valueOf(sNumberArray[i]);
}
if(sNumber.length()==iIndex+1&&iIndex='-'){
sNumber+=".";
}
elseif(sNumber.length()==iIndex+2&&iIndexsNumber+=".";