Java 为什么eval类会给我一个从int到double的施法错误?

Java 为什么eval类会给我一个从int到double的施法错误?,java,function,eval,scriptmanager,integral,Java,Function,Eval,Scriptmanager,Integral,我试图用一个字符串公式来做一个方法,并用一个很小的区间来求黎曼和,从而求出这个公式的积分。我使用ScriptEngine和ScriptEngineManager类(使用eval()方法)对函数求值。出于某种原因,我遇到了以下错误: 线程“main”java.lang.ClassCastException中的异常:无法将java.lang.Integer转换为java.lang.Double at sum.integral(sum.java:31) at sum.main(sum.java:13)

我试图用一个字符串公式来做一个方法,并用一个很小的区间来求黎曼和,从而求出这个公式的积分。我使用ScriptEngine和ScriptEngineManager类(使用eval()方法)对函数求值。出于某种原因,我遇到了以下错误:

线程“main”java.lang.ClassCastException中的异常:无法将java.lang.Integer转换为java.lang.Double at sum.integral(sum.java:31) at sum.main(sum.java:13)

导入java.beans.Expression;
导入javax.script.ScriptEngine;
导入javax.script.ScriptEngineManager;
导入javax.script.ScriptException;
公共类金额{
//测试方法
公共静态void main(字符串[]args)引发脚本异常{
双x=积分(“5*x^2”,0,5);
系统输出println(x);
}
公共静态二重积分(字符串函数、双下、双上)引发ScriptException
{
双倍合计=0;
ScriptEngineManager mgr=新建ScriptEngineManager();
ScriptEngine=mgr.getEngineByName(“JavaScript”);
//以.001的间隔从上到下求解函数,并将其添加到总数中。
对于(双i=较低;i<较高;i+=.001)
{
//计算时间间隔
发动机。放置(“x”,i);
总+=(双)发动机评估(功能);
}
返回总数;
}
}

您的JS代码段返回一个
整数(*),因为
x^2
在JavaScript中不是获得2次幂的正确方法。改为尝试
5*Math.pow(x,2)
,表达式将返回一个
Double

在JavaScript中,
^
运算符是

此外,计算积分的循环错误,需要乘以矩形宽度:

    double delta = 0.001;
    for (double i = lower; i < upper; i += delta) {
        //evaluates the interval
        engine.put("x", i);
        total += delta * ((Number) engine.eval(function)).doubleValue();
    }
double delta=0.001;
对于(双i=较低;i<较高;i+=增量){
//计算时间间隔
发动机。放置(“x”,i);
总计+=增量*((数字)引擎.eval(函数)).doubleValue();
}
(*)请参阅以获取初步解释。但在评论中,@A.Sundararajan对此提供了证据。我没有调查确切的原因,我只是观察到我得到了一个
整数
,只是猜测在表达式中使用位运算(来自OP的原始代码)触发了到整数的转换。我最初编辑我的帖子是为了包含对“数学错误”的修正,但是David较新的答案(大约4分钟^^^^)对于原始问题来说更完整,应该仍然是被接受的答案。Nashorn使用(因为),所以在不需要双精度时,它将使用整数。因此,您不能指望它返回双精度

另外,
5*x^2
在JavaScript中表示“五乘x或二”。
**
求幂运算符是在较新版本的JavaScript语言中定义的,但Nashorn还不支持它

如果将JavaScript代码更改为
5*x*x
,它将正常工作,但这样做更安全:

total += 0.001 * ((Number)engine.eval(function)).doubleValue();
编译常用代码

由于在循环中重复调用此函数,因此最佳做法是提前编译该函数。这种性能优化并不是绝对必要的,但是引擎每次都必须编译您的函数(尽管它可能会使用缓存来帮助)

使用ES6语言功能

将来,当Nashorn确实支持
**
操作符时,如果您想使用它,您可能需要打开ES6功能,如下所示:

import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine enjin = factory.getScriptEngine("--language=es6");
java -Dnashorn.args=--language=es6
或者像这样:

import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine enjin = factory.getScriptEngine("--language=es6");
java -Dnashorn.args=--language=es6

*编辑以解释评论中指出的数学修正。

因此这似乎有效,但我得到了一个显著的舍入误差或其他东西。我还将total乘以了.001的矩形长度。是的,代码中缺少这个乘以delta(矩形宽度)的乘法,但是我得到了预期的错误。例如,如果我将表达式简化为[0,1]区间上的
x*x
,确切结果是
1/3
,对于delta=
0.001
,我得到
0.332833500000000095
,对于delta=
0.0001
,我得到
0.3333833349999431
。对我来说这看起来“很好”。@Hugues Moreau-太好了,谢谢!另一个问题——用同样的想法计算三角函数和指数/对数函数是否可行?小修正:乐观类型仅在jdk 9中默认为true。在jdk8u中,乐观类型默认为false。也就是说,请不要期望David Conrad提到的JS数字的Java数字类型值。但是,在JDK8U中,该命令行标志的默认值设置为false(现在仍然为false)。在JDK9中,乐观类型的默认值为true。所以这个解决方案也很有效,但我遇到了另一个问题。由于Java,似乎有一个非常大的舍入误差,因为当我计算这个值时,得到的是205.867,而不是208和1/3,这才是真正的解决方案。这有什么问题吗?不是四舍五入错误,而是数学错误:)请参阅我的更新答案,以了解该部分的修复方法。可能值得注意的是,
for(double i=lower;i
将累积错误,因为
0.001
不能精确地用浮点表示。(这可能会也可能不会影响你结果的准确性;我不确定。)我无法想象为什么有人会否决这个问题。Erik提供了他的代码、他遇到的问题以及他收到的准确错误消息。用户还可以在问题中提出什么要求?