Android 字符串求值工具(科学计算)

在Android数学计算APP,经常有场景让用户输入一串表达式,并且计算其值,如果手动用 Integer.parseInt 不太好用而且还需要 try。于是写了这个库。

功能:接受一个字符串String表达式(支持科学运算),返回表达式计算结果:double类型数字;

为了提升效率,代码是用C++写的,通过JNI链接到Java,但是链接也需要花时间,所以在密集计算时才能体现优势。可以用此工具写函数图像的APP

.
.

1、依赖

github地址:https://github.com/sandyz987/CalculatorJNI 欢迎star~~

build.gradle 加入:

	allprojects {
		repositories {
			...
			maven { url 'https://jitpack.io' }
		}
	}
	dependencies {
	        implementation 'com.github.sandyz987:CalculatorJNI:1.1.0'
	}

.
.

2、使用

如果表达式错误,不会报错而是输出一个错误的值!请知晓。后续版本会更新。表达式中仅支持小写字符。

Android 字符串求值工具(科学计算)

直接计算表达式的值:

var calculator: Calculator? = Calculator() // 新建计算器实例

// calculator?.setExpression("1+2*3") // 设置常规表达式

calculator?.setExpression("if(-1,1==2|1!=2,sin(-1.57))") // 设置带函数的表达式

println(calculator?.getAns()) // 执行计算,获得计算结果

calculator?.destroy() // 因为底层是c++实现,所以防止内存泄露要执行destroy()

calculator = null // 已经destroy的calculator不能再使用,置为null以便jvm回收

上面的表达式中,if( 三个参数 ),第一个参数是条件,如果非零,整个if语句的值就是第二个参数的计算结果,否则为第三个表达式计算的结果。

可见-1!=0最终表达式计算的结果为:第二个参数的结果。为1(条件表达式的结果为真则返回1)。

在表达式中使用变量:

var sum = 0.0

var calculator: Calculator? = Calculator() // 新建计算器实例

calculator?.setExpression("if(i<=500,sin(i),max(750,i)") // 设置要计算的表达式,包含可变变量‘i’,默认值为0,计算时请设置变量值

for (i in 1..1000) {
      calculator?.setVariable('i', i.toDouble()) // 设置表达式中i的值为 for循环i的值
      val ans = calculator?.getAns() // 执行计算
      sum += ans?: 0.0
}

println(sum)

calculator?.destroy()

变量仅支持a-z的字符。

setExpression方法会进行对字符串的预处理操作,此方法效率低下,避免在循环中使用。getAns方法效率较高。

.
.

3、支持的函数

逻辑表达式(运算符):

当一个逻辑表达式成立时,表达式的值为1,如“2==2|1=-1”的值是1,否则为零。

当一个表达式的结果为零,认为表达式为假,否则为真

​ >, <, == (注意是双等于号), <= ,>= ,& (and) ,| (or) ,~ (not)

数学运算符:

​ +, -, *, /, % (求模), ^ (乘方), ! (阶乘), # (负号,一般会自动区分负号和减号,无需用户输入井号)

符号:

​ 括号(改变计算顺序),逗号 (用于分割函数参数)

函数:

sin(x)

​ 三角函数 sin(x) cos(x) tan(x) asin(x) acos(x) atan(x)
​ 如果不在定义域则返回0

rand(x,y)

​ 随机数 rand(x,y)(返回[x,y]间的整数,需要srand)

deg(x)

​ 弧度转角度 deg(x) 角度转弧度 rad(x)

if(条件,x,y)

​ 条件表达式 if(条件,x,y),其中当x非0,则返回表达式x的值,否则返回表达式y的值

log(x,y)

​ 对数 log(x,y)返回以x为底y的对数 ln(x)返回以e为底的自然对数

exp(x)

​ 自然数的n次方 exp(x),返回e^x

max(x,y)

​ 最大最小值 max(x,y)返回表达式x和表达式y的较大的一个,min相反

sign(x)

​ 取符号 sign(x) 如果x大于等于0,返回1,否则返回-1

round(x)

​ 四舍五入 round(x) 向下取整 floor(x)

abs(x)

​ 绝对值 abs(x) 返回x的绝对值

sqrt(x)

​ 开平方 sqrt(x) 返回x开平方的值
​ 开n次方可以用运算符“ ^ ”代替
.
.

4、源码思路

​ 这里我用例子解释。
​ 假如用户输入“-sin(12.57)-cos(-6)”
​ 规定:如果减号“-”出现在符号后面(不包括右括号)或者出现在字符串开头,就替换为负号“#”,负号是一元运算符,减号是二元运算符,所以有必要区分。
​ 故原字符串被替换为“#sin(12.57)-cos(#6)”
​ 由于sin,cos是多字符不好处理,则替换为单字符(这里我用A-Z),故替换为“#J(12.57)-K(#6)”
​ 接下来只需要区分是符号还是数字即可。

读入#是符号
读入J是符号
读入(是符号(以上都是转后缀表达式的标准操作,不再赘述)
读入1是数字,不确定是不是一个完整的数字,则存在number里,
读入2是数字,将原来的number*10+2。
读入.表明接下来的数字是小数部分了。
读入5,number+=pow(10,-1)*5
读入7,number+=pow(10,-2)*7
当读入符号或者到字符串末尾时,讲存的数字保存下来,并且清空。
以此类推

​ 这样子符号和数字就被区分开了,并且转为后缀表达式了。
​ 接下来就只需要运行后缀表达式的求值和控制好优先级了。
​ 因为没检测表达式是否符合规范,以下的写法也能计算出正确结果:

sin7
7sin
(7)sin

暂时未写语法分析,如果用户输入错误的表达式,则计算结果可能会有错误,表达式正确则结果正确。

5、赞助

​ 它花费了我许多的工作之余的时间!如果它帮助到您了的话,欢迎请我喝杯咖啡:)

​ [二维码]

上一篇:"当前不会命中断点,没有与此行关联的可执行代码"可能和"断点位置不准确"有关


下一篇:Calculator Conundrum UVA - 11549(floyd判圈)